Skip to content

Commit

Permalink
chore: Update components
Browse files Browse the repository at this point in the history
  • Loading branch information
BayBreezy committed Nov 4, 2024
1 parent 3fb1c6b commit 480292b
Show file tree
Hide file tree
Showing 20 changed files with 1,126 additions and 57 deletions.
105 changes: 66 additions & 39 deletions app/components/Ui/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,94 @@
:is="elementType"
:class="
buttonStyles({
hasIcon: !!icon,
disabled: disabled || loading,
variant: variant,
size: size,
class: props.class,
})
"
:disabled="disabled || loading"
:to="to"
:href="href"
:type="type"
@click="onClick"
v-bind="forwarded"
>
<slot>{{ text }}</slot>
<slot name="iconLeft">
<div
v-if="icon && iconPlacement == 'left'"
className="w-0 flex items-center shrink-0 justify-center translate-x-[0%] pr-0 opacity-0 transition-all duration-200 group-hover:w-6 group-hover:translate-x-100 group-hover:pr-2 group-hover:opacity-100"
>
<Icon :name="icon" class="size-5" />
</div>
</slot>
<slot>
<span v-if="text">{{ text }}</span>
</slot>
<slot name="iconRight">
<div
v-if="icon && iconPlacement == 'right'"
className="w-0 flex items-center justify-center shrink-0 translate-x-[100%] pl-0 opacity-0 transition-all duration-200 group-hover:w-6 group-hover:translate-x-0 group-hover:pl-2 group-hover:opacity-100"
>
<Icon :name="icon" class="size-5" />
</div>
</slot>
</component>
</template>

<script setup lang="ts">
import type { RouteLocationRaw } from "vue-router";
import { reactiveOmit } from "@vueuse/core";
import { useForwardProps } from "radix-vue";
import type { NuxtLinkProps } from "#app/components";
type ButtonProps = VariantProps<typeof buttonStyles>;
const props = withDefaults(
defineProps<{
/** The type fro the button */
type?: "button" | "submit" | "reset";
/** Whether the button is disabled */
disabled?: boolean;
/** Whether the button is loading */
loading?: boolean;
/** The action to perform when the button is clicked */
onClick?: any;
/** The location to navigate to when the button is clicked */
to?: string | RouteLocationRaw;
/** The location to navigate to when the button is clicked */
href?: string;
/** The element to render the button as */
as?: string;
/** Custom class(es) to add to parent element */
class?: any;
/** The variant of the button */
variant?: ButtonProps["variant"];
/** The size of the button */
size?: ButtonProps["size"];
/** The text to display in the button */
text?: string;
}>(),
defineProps<
NuxtLinkProps & {
/** The type fro the button */
type?: "button" | "submit" | "reset";
/** Whether the button is disabled */
disabled?: boolean;
/** Whether the button is loading */
loading?: boolean;
/** The action to perform when the button is clicked */
onClick?: any;
/** The element to render the button as */
as?: string;
/** Custom class(es) to add to parent element */
class?: any;
/** The variant of the button */
variant?: ButtonProps["variant"];
/** The size of the button */
size?: ButtonProps["size"];
/** The text to display in the button */
text?: string;
/** Should the icon be displayed on the `left` or the `right`? */
iconPlacement?: "left" | "right";
/** The icon to display in the button */
icon?: string;
}
>(),
{
type: "button",
onClick: undefined,
to: undefined,
href: undefined,
as: undefined,
class: undefined,
text: undefined,
variant: "default",
size: "default",
}
);
const elementType = computed(() => {
if (props.as) return props.as;
if (props.href || props.to) return resolveComponent("NuxtLink");
if (props.href || props.to || props.target) return resolveComponent("NuxtLink");
return "button";
});
const forwarded = useForwardProps(
reactiveOmit(
props,
"class",
"text",
"icon",
"iconPlacement",
"size",
"variant",
"as",
"loading",
"disabled"
)
);
</script>
241 changes: 241 additions & 0 deletions app/components/Ui/Chart/Area.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
<template>
<div :class="styles({ class: props.class })">
<UiChartLegend
v-if="showLegend"
v-model:items="legendItems"
@legend-item-click="handleLegendItemClick"
/>

<VisXYContainer
:style="{ height: isMounted ? '100%' : 'auto' }"
:margin="{ left: 20, right: 20 }"
:data="data"
>
<svg width="0" height="0">
<defs>
<linearGradient
v-for="(color, i) in colors"
:id="`${chartRef}-color-${i}`"
:key="i"
x1="0"
y1="0"
x2="0"
y2="1"
>
<template v-if="showGradiant">
<stop offset="5%" :stop-color="color" stop-opacity="0.4" />
<stop offset="95%" :stop-color="color" stop-opacity="0" />
</template>
<template v-else>
<stop offset="0%" :stop-color="color" />
</template>
</linearGradient>
</defs>
</svg>

<UiChartCrosshair
v-if="showTooltip"
:colors="colors"
:items="legendItems"
:index="index"
:custom-tooltip="customTooltip"
/>

<template v-for="(category, i) in categories" :key="category">
<VisArea
:x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]"
color="auto"
:curve-type="curveType"
:attributes="{
[Area.selectors.area]: {
fill: `url(#${chartRef}-color-${i})`,
},
}"
:opacity="
legendItems.find((item) => item.name === category)?.inactive ? filterOpacity : 1
"
/>
</template>

<template v-for="(category, i) in categories" :key="category">
<VisLine
:x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]"
:color="colors[i]"
:curve-type="curveType"
:attributes="{
[Line.selectors.line]: {
opacity: legendItems.find((item) => item.name === category)?.inactive
? filterOpacity
: 1,
},
}"
/>
</template>

<VisAxis
v-if="showXAxis"
type="x"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
tick-text-color="hsl(var(--vis-text-color))"
/>
<VisAxis
v-if="showYAxis"
type="y"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"
:grid-line="showGridLine"
:attributes="{
[Axis.selectors.grid]: {
class: 'text-muted',
},
}"
tick-text-color="hsl(var(--vis-text-color))"
/>

<slot />
</VisXYContainer>
</div>
</template>

<script lang="ts">
import { Area, Axis, CurveType, Line } from "@unovis/ts";
import { VisArea, VisAxis, VisLine, VisXYContainer } from "@unovis/vue";
import type { BulletLegendItemInterface, Spacing } from "@unovis/ts";
type KeyOf<T extends Record<string, any>> = Extract<keyof T, string>;
export interface AreaChartProps<T extends Record<string, any>> {
/**
* The source data, in which each entry is a dictionary.
*/
data: T[];
/**
* Select the categories from your data. Used to populate the legend and toolip.
*/
categories: KeyOf<T>[];
/**
* Sets the key to map the data to the axis.
*/
index: KeyOf<T>;
/**
* Change the default colors.
*/
colors?: string[];
/**
* Margin of each the container
*/
margin?: Spacing;
/**
* Change the opacity of the non-selected field
* @default 0.2
*/
filterOpacity?: number;
/**
* Function to format X label
*/
xFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string;
/**
* Function to format Y label
*/
yFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string;
/**
* Controls the visibility of the X axis.
* @default true
*/
showXAxis?: boolean;
/**
* Controls the visibility of the Y axis.
* @default true
*/
showYAxis?: boolean;
/**
* Controls the visibility of tooltip.
* @default true
*/
showTooltip?: boolean;
/**
* Controls the visibility of legend.
* @default true
*/
showLegend?: boolean;
/**
* Controls the visibility of gridline.
* @default true
*/
showGridLine?: boolean;
}
</script>

<script setup lang="ts" generic="T extends Record<string, any>">
const styles = tv({
base: "flex h-[400px] w-full flex-col items-end",
});
const props = withDefaults(
defineProps<
AreaChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component;
/**
* Type of curve
*/
curveType?: CurveType;
/**
* Controls the visibility of gradient.
* @default true
*/
showGradiant?: boolean;
/**
* Additional class to be added to the container.
*/
class?: any;
}
>(),
{
curveType: CurveType.MonotoneX,
filterOpacity: 0.2,
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
showXAxis: true,
showYAxis: true,
showTooltip: true,
showLegend: true,
showGridLine: true,
showGradiant: true,
}
);
const emits = defineEmits<{
legendItemClick: [d: BulletLegendItemInterface, i: number];
}>();
type KeyOfT = Extract<keyof T, string>;
type Data = (typeof props.data)[number];
const chartRef = useId();
const index = computed(() => props.index as KeyOfT);
const colors = computed(() =>
props.colors?.length ? props.colors : defaultColors(props.categories.length)
);
const legendItems = ref<BulletLegendItemInterface[]>(
props.categories.map((category, i) => ({
name: category,
color: colors.value[i],
inactive: false,
}))
);
const isMounted = useMounted();
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
emits("legendItemClick", d, i);
}
</script>
Loading

0 comments on commit 480292b

Please sign in to comment.