@saasmakers/ui 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,6 @@ defineSlots<{
20
20
  }>()
21
21
 
22
22
  const { t } = useI18n()
23
- const { getIcon } = useLayerIcons()
24
23
  const isClosed = ref(false)
25
24
 
26
25
  const buttonColor = computed<BaseColor>(() => {
@@ -89,7 +88,7 @@ async function onClose(event: MouseEvent) {
89
88
  <BaseButton
90
89
  v-if="isClosable"
91
90
  :color="buttonColor"
92
- :icon="getIcon('closeCircle')"
91
+ :icon="getLayerIcon('closeCircle')"
93
92
  light
94
93
  size="xs"
95
94
  :text="t('closeThisMessage')"
@@ -35,8 +35,6 @@ defineSlots<{
35
35
  right?: () => VNode[]
36
36
  }>()
37
37
 
38
- const { getIcon } = useLayerIcons()
39
-
40
38
  const hasAvatarBox = computed<boolean>(() => {
41
39
  return !!(props.avatar || props.emoji || props.icon || props.image)
42
40
  })
@@ -199,7 +197,7 @@ function onClick(event: MouseEvent) {
199
197
  'text-2xl': size === 'lg',
200
198
  }"
201
199
  color="green"
202
- :icon="getIcon('checkCircle')"
200
+ :icon="getLayerIcon('checkCircle')"
203
201
  />
204
202
 
205
203
  <BaseIcon
@@ -210,7 +208,7 @@ function onClick(event: MouseEvent) {
210
208
  'text-xl': size === 'base',
211
209
  'text-2xl': size === 'lg',
212
210
  }"
213
- :icon="getIcon('chevronRight')"
211
+ :icon="getLayerIcon('chevronRight')"
214
212
  />
215
213
 
216
214
  <slot name="right" />
@@ -18,7 +18,6 @@ const emit = defineEmits<{
18
18
  }>()
19
19
 
20
20
  const { t } = useI18n()
21
- const { getIcon } = useLayerIcons()
22
21
 
23
22
  function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection) {
24
23
  if (!props.loading) {
@@ -59,7 +58,7 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
59
58
  v-if="navigable && !loading && !hidePrevious"
60
59
  class="mr-2 shrink-0 hover:!no-underline"
61
60
  clickable
62
- :icon="getIcon('chevronLeft')"
61
+ :icon="getLayerIcon('chevronLeft')"
63
62
  size="xs"
64
63
  :text="t('previous')"
65
64
  @click="onNavigate($event, 'previous')"
@@ -127,7 +126,7 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
127
126
  class="ml-2 shrink-0 hover:!no-underline"
128
127
  clickable
129
128
  reverse
130
- :icon="getIcon('chevronRight')"
129
+ :icon="getLayerIcon('chevronRight')"
131
130
  size="xs"
132
131
  :text="t('next')"
133
132
  @click="onNavigate($event, 'next')"
@@ -25,7 +25,6 @@ const emit = defineEmits<{
25
25
 
26
26
  const confirming = ref(false)
27
27
  const { t } = useI18n()
28
- const { getIcon } = useLayerIcons()
29
28
 
30
29
  const isClickable = computed(() => {
31
30
  return props.clickable || props.to || props.confirmation
@@ -34,16 +33,16 @@ const isClickable = computed(() => {
34
33
  const statusIcon = computed<string | undefined>(() => {
35
34
  switch (props.status) {
36
35
  case 'error': {
37
- return getIcon('closeCircle')
36
+ return getLayerIcon('closeCircle')
38
37
  }
39
38
  case 'info': {
40
- return getIcon('infoCircle')
39
+ return getLayerIcon('infoCircle')
41
40
  }
42
41
  case 'success': {
43
- return getIcon('checkCircle')
42
+ return getLayerIcon('checkCircle')
44
43
  }
45
44
  case 'warning': {
46
- return getIcon('exclamationCircle')
45
+ return getLayerIcon('exclamationCircle')
47
46
  }
48
47
  default: {
49
48
  return undefined
@@ -10,18 +10,16 @@ const props = withDefaults(defineProps<BaseMetric>(), {
10
10
  value: undefined,
11
11
  })
12
12
 
13
- const { getIcon } = useLayerIcons()
14
-
15
13
  const performanceIcon = computed<string | undefined>(() => {
16
14
  switch (props.performance) {
17
15
  case 'down': {
18
- return getIcon('arrowDown')
16
+ return getLayerIcon('arrowDown')
19
17
  }
20
18
  case 'equal': {
21
- return getIcon('arrowRight')
19
+ return getLayerIcon('arrowRight')
22
20
  }
23
21
  case 'up': {
24
- return getIcon('arrowUp')
22
+ return getLayerIcon('arrowUp')
25
23
  }
26
24
  default: {
27
25
  return undefined
@@ -51,7 +49,7 @@ const performanceIcon = computed<string | undefined>(() => {
51
49
  v-if="performanceTooltip"
52
50
  v-tooltip="performanceTooltip"
53
51
  class="mr-1"
54
- :icon="getIcon('infoCircle')"
52
+ :icon="getLayerIcon('infoCircle')"
55
53
  />
56
54
 
57
55
  <BaseText
@@ -2,7 +2,6 @@
2
2
  import { onKeyStroke } from '@vueuse/core'
3
3
  import { Motion } from 'motion-v'
4
4
  import type { BaseOverlay } from '../../types/bases'
5
- import useMotion from '../../composables/useMotion'
6
5
 
7
6
  const props = withDefaults(defineProps<BaseOverlay>(), {
8
7
  active: true,
@@ -22,9 +21,6 @@ defineSlots<{
22
21
  default?: () => VNode[]
23
22
  }>()
24
23
 
25
- const { getIcon } = useLayerIcons()
26
- const { fadeIn } = useMotion()
27
-
28
24
  const isClickable = computed(() => {
29
25
  return props.clickable || props.hasClose
30
26
  })
@@ -65,7 +61,7 @@ onKeyStroke('Escape', (event) => {
65
61
  v-if="hasClose"
66
62
  class="pointer-events-auto absolute right-4 top-4 z-50 text-gray-200 dark:text-gray-800"
67
63
  clickable
68
- :icon="getIcon('close')"
64
+ :icon="getLayerIcon('close')"
69
65
  @click="onClose"
70
66
  />
71
67
 
@@ -29,7 +29,6 @@ defineSlots<{
29
29
 
30
30
  const { locale, t } = useI18n()
31
31
  const { translatedContent } = useTranslation(useSlots(), locale)
32
- const { getIcon } = useLayerIcons()
33
32
  const closed = ref(false)
34
33
 
35
34
  const finalBackground = computed(() => {
@@ -148,7 +147,7 @@ function onClose(event: MouseEvent) {
148
147
  'ml-3': size === 'sm',
149
148
  'ml-4': size === 'base',
150
149
  }"
151
- :icon="getIcon('close')"
150
+ :icon="getLayerIcon('close')"
152
151
  @click="onClose"
153
152
  />
154
153
  </div>
@@ -11,8 +11,6 @@ const emit = defineEmits<{
11
11
  trigger: [event: KeyboardEvent]
12
12
  }>()
13
13
 
14
- const { getIcon } = useLayerIcons()
15
-
16
14
  function isTypingTarget(target: EventTarget | null) {
17
15
  const element = target as HTMLElement | null
18
16
  const typingTags = ['INPUT', 'SELECT', 'TEXTAREA']
@@ -53,7 +51,7 @@ onKeyStroke(
53
51
  <Icon
54
52
  v-if="shortcut.toLowerCase() === 'enter'"
55
53
  class="size-2"
56
- :name="getIcon('enter')"
54
+ :name="getLayerIcon('enter')"
57
55
  />
58
56
 
59
57
  <template v-else>
@@ -35,7 +35,6 @@ defineSlots<{
35
35
  default?: () => VNode[]
36
36
  }>()
37
37
 
38
- const { getIcon } = useLayerIcons()
39
38
  const hovered = ref(false)
40
39
  const form = reactive({ name: '' })
41
40
 
@@ -136,7 +135,7 @@ function onRemove(event: MouseEvent) {
136
135
  class="js-drag-handle mr-2 cursor-move"
137
136
  clickable
138
137
  color="gray"
139
- :icon="getIcon('drag')"
138
+ :icon="getLayerIcon('drag')"
140
139
  />
141
140
 
142
141
  <span
@@ -190,7 +189,7 @@ function onRemove(event: MouseEvent) {
190
189
  <BaseIcon
191
190
  v-if="removable"
192
191
  class="ml-1.5 text-red-700 dark:text-red-300 hover:text-black dark:hover:text-white"
193
- :icon="getIcon('closeCircle')"
192
+ :icon="getLayerIcon('closeCircle')"
194
193
  @click.prevent.stop="onRemove"
195
194
  />
196
195
  </component>
@@ -34,7 +34,6 @@ const root = ref<HTMLDivElement>()
34
34
  const showingAllTags = ref(false)
35
35
  const showingTagCreationField = ref(false)
36
36
  const { t } = useI18n()
37
- const { getIcon } = useLayerIcons()
38
37
 
39
38
  const sortedTags = computed({
40
39
  get() {
@@ -143,7 +142,7 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
143
142
  'm-1': size === 'base',
144
143
  }"
145
144
  color="indigo"
146
- :icon="getIcon('back')"
145
+ :icon="getLayerIcon('back')"
147
146
  :size="size"
148
147
  :text="t('cancel')"
149
148
  @click="onGoBack"
@@ -158,7 +157,7 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
158
157
  }"
159
158
  color="indigo"
160
159
  :editable="showingTagCreationField"
161
- :icon="showingTagCreationField ? getIcon('tags') : getIcon('plus')"
160
+ :icon="showingTagCreationField ? getLayerIcon('tags') : getLayerIcon('plus')"
162
161
  is-creation
163
162
  :light="false"
164
163
  :size="size"
@@ -14,8 +14,6 @@ const emit = defineEmits<{
14
14
  close: [event: KeyboardEvent | MouseEvent, toast: BaseToast]
15
15
  }>()
16
16
 
17
- const { getIcon } = useLayerIcons()
18
-
19
17
  function onAction(event: KeyboardEvent | MouseEvent) {
20
18
  if (props.action) {
21
19
  emit('action', event, props)
@@ -82,7 +80,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
82
80
  'group-hover:text-green-600 dark:group-hover:text-green-400': status === 'success',
83
81
  'group-hover:text-orange-600 dark:group-hover:text-orange-400': status === 'warning',
84
82
  }"
85
- :icon="getIcon('close')"
83
+ :icon="getLayerIcon('close')"
86
84
  />
87
85
  </template>
88
86
  </div>
@@ -10,7 +10,6 @@ const emit = defineEmits<{
10
10
  }>()
11
11
 
12
12
  const { locale, t } = useI18n()
13
- const { normalizeText } = useLayerUtils()
14
13
  const modelValue = defineModel<FieldEmojis['modelValue']>({ default: '' })
15
14
  const searchRaw = ref('')
16
15
  const searchQuery = refDebounced(searchRaw, 250)
@@ -31,9 +31,6 @@ const emit = defineEmits<{
31
31
  optionClick: [event: MouseEvent, value: string]
32
32
  }>()
33
33
 
34
- const { getIcon } = useLayerIcons()
35
- const { fadeIn } = useMotion()
36
- const { normalizeText } = useLayerUtils()
37
34
  const { t } = useI18n()
38
35
  const id = useId()
39
36
  const modelValue = defineModel<FieldSelect['modelValue']>({ default: '' })
@@ -264,7 +261,7 @@ function selectOption(event: MouseEvent, value: string) {
264
261
  class="ml-2 flex-initial"
265
262
  :class="{ 'rotate-180': opened }"
266
263
  color="gray"
267
- :icon="getIcon('arrowDown')"
264
+ :icon="getLayerIcon('arrowDown')"
268
265
  />
269
266
  </div>
270
267
 
@@ -320,7 +317,7 @@ function selectOption(event: MouseEvent, value: string) {
320
317
  <BaseIcon
321
318
  class="pointer-events-none"
322
319
  color="gray"
323
- :icon="getIcon('infoCircle')"
320
+ :icon="getLayerIcon('infoCircle')"
324
321
  :size="size"
325
322
  :text="t('noResults')"
326
323
  />
@@ -19,7 +19,6 @@ const emit = defineEmits<{
19
19
  blur: [event: FocusEvent, value: string, name?: string]
20
20
  }>()
21
21
 
22
- const { getIcon } = useLayerIcons()
23
22
  const id = useId()
24
23
  const modelValue = defineModel<FieldTime['modelValue']>({ default: '' })
25
24
  const inputRef = ref<HTMLInputElement>()
@@ -75,7 +74,7 @@ function onFieldBlur(event: FocusEvent) {
75
74
  @keydown.space.prevent="onContainerKeyDown"
76
75
  >
77
76
  <BaseIcon
78
- :icon="getIcon('clock')"
77
+ :icon="getLayerIcon('clock')"
79
78
  :size="size"
80
79
  />
81
80
 
@@ -1,5 +1,4 @@
1
1
  <script lang="ts" setup>
2
- const { fadeInUp } = useMotion()
3
2
  const toasts = useToasts()
4
3
 
5
4
  const activeShortcutToastId = computed(() => {
@@ -101,8 +101,8 @@ declare global {
101
101
  type LayoutModal = import('./layout').LayoutModal
102
102
 
103
103
  // Project
104
- type LayerIconIcon = import('../composables/useLayerIcons').LayerIconIcon
105
- type LayerIconValue = import('../composables/useLayerIcons').LayerIconValue
104
+ type LayerIconIcon = import('../utils/layerIcons').LayerIconIcon
105
+ type LayerIconValue = import('../utils/layerIcons').LayerIconValue
106
106
 
107
107
  // Navigator
108
108
  interface Navigator {
@@ -0,0 +1,31 @@
1
+ export const fadeIn = {
2
+ animate: {
3
+ opacity: 1,
4
+ transition: { duration: 0.25 },
5
+ },
6
+ initial: { opacity: 0 },
7
+ }
8
+
9
+ export const fadeInLeft = {
10
+ animate: {
11
+ opacity: 1,
12
+ transition: { duration: 0.25 },
13
+ x: 0,
14
+ },
15
+ initial: {
16
+ opacity: 0,
17
+ x: -25,
18
+ },
19
+ }
20
+
21
+ export const fadeInUp = {
22
+ animate: {
23
+ opacity: 1,
24
+ transition: { duration: 0.25 },
25
+ y: 0,
26
+ },
27
+ initial: {
28
+ opacity: 0,
29
+ y: 25,
30
+ },
31
+ }
@@ -0,0 +1,126 @@
1
+ import type { AreaDrawEvent, BarChartOptions, BarDrawEvent, BaseChart, DrawEvent, LineDrawEvent, Options } from 'chartist'
2
+ import { easings } from 'chartist'
3
+
4
+ type EasingType = keyof typeof easings | number[]
5
+
6
+ export function progressiveLinePlugin(params: {
7
+ animateArea?: boolean
8
+ delay?: number
9
+ duration?: number
10
+ easing?: EasingType
11
+ stagger?: number
12
+ } = {}) {
13
+ const animateArea = params.animateArea ?? true
14
+ const delay = params.delay ?? 0
15
+ const duration = params.duration ?? 500
16
+ const easing = params.easing ?? easings.easeOutQuart
17
+ const stagger = params.stagger ?? 0
18
+
19
+ return (chart: BaseChart) => {
20
+ chart.on('draw', (ctx: DrawEvent) => {
21
+ const begin = delay + (stagger ? stagger * (ctx.seriesIndex ?? 0) : 0)
22
+
23
+ // Animation for line charts
24
+ if (ctx.type === 'line') {
25
+ const lineCtx = ctx as LineDrawEvent
26
+ const node = lineCtx.element.getNode<SVGPathElement>()
27
+ const length = node.getTotalLength()
28
+
29
+ // Set the stroke dasharray and dashoffset
30
+ lineCtx.element.attr({
31
+ 'stroke-dasharray': `${length}px ${length}px`,
32
+ 'stroke-dashoffset': `${length}px`,
33
+ })
34
+
35
+ // Animate the stroke dashoffset to 0
36
+ lineCtx.element.animate({
37
+ 'stroke-dashoffset': {
38
+ begin,
39
+ dur: duration,
40
+ easing,
41
+ fill: 'freeze',
42
+ from: `${length}px`,
43
+ to: '0px',
44
+ },
45
+ }, false)
46
+
47
+ // Clean up the dash attributes after it finishes
48
+ const total = delay + duration + (stagger ? stagger * (lineCtx.seriesIndex ?? 0) : 0)
49
+
50
+ globalThis.setTimeout(() => {
51
+ lineCtx.element.attr({
52
+ 'stroke-dasharray': undefined,
53
+ 'stroke-dashoffset': undefined,
54
+ })
55
+ }, total + 30)
56
+ }
57
+
58
+ // Animation for area charts
59
+ if (animateArea && ctx.type === 'area') {
60
+ const areaCtx = ctx as AreaDrawEvent
61
+
62
+ areaCtx.element.animate({
63
+
64
+ d: {
65
+ begin,
66
+ dur: duration,
67
+ easing,
68
+ fill: 'freeze',
69
+ from: areaCtx.path
70
+ .clone()
71
+ .scale(1, 0)
72
+ .translate(0, areaCtx.chartRect.height())
73
+ .stringify(),
74
+ to: areaCtx.path.stringify(),
75
+ },
76
+ }, false)
77
+ }
78
+
79
+ // Animation for bar charts
80
+ if (ctx.type === 'bar') {
81
+ const barCtx = ctx as BarDrawEvent
82
+
83
+ // Access private options property via type assertion
84
+ const chartOptions = (chart as unknown as {
85
+ options?: Options & Partial<BarChartOptions>
86
+ }).options
87
+
88
+ const horizontal = !!(chartOptions as BarChartOptions | undefined)?.horizontalBars
89
+
90
+ // For vertical bars, y1 is baseline, y2 is top. For horizontal, x1 is baseline, x2 is end.
91
+ const from = horizontal ? barCtx.x1 : barCtx.y1
92
+ const to = horizontal ? barCtx.x2 : barCtx.y2
93
+
94
+ // Start collapsed at baseline
95
+ if (horizontal) {
96
+ barCtx.element.attr({ x2: from })
97
+
98
+ barCtx.element.animate({
99
+ x2: {
100
+ begin,
101
+ dur: duration,
102
+ easing,
103
+ fill: 'freeze',
104
+ from: String(from),
105
+ to: String(to),
106
+ },
107
+ }, false)
108
+ }
109
+ else {
110
+ barCtx.element.attr({ y2: from })
111
+
112
+ barCtx.element.animate({
113
+ y2: {
114
+ begin,
115
+ dur: duration,
116
+ easing,
117
+ fill: 'freeze',
118
+ from: String(from),
119
+ to: String(to),
120
+ },
121
+ }, false)
122
+ }
123
+ }
124
+ })
125
+ }
126
+ }
@@ -1,20 +1,13 @@
1
1
  import numbroLib from 'numbro'
2
2
 
3
- export default function useLayerUtils() {
4
- return {
5
- normalizeText,
6
- numbro,
7
- }
8
- }
9
-
10
- function normalizeText(text: string) {
3
+ export function normalizeText(text: string) {
11
4
  return text
12
5
  .toLowerCase()
13
6
  .normalize('NFD')
14
7
  .replaceAll(/[\u0300-\u036F]/g, '')
15
8
  }
16
9
 
17
- function numbro(number: '∞' | number | undefined, format?: string) {
10
+ export function numbro(number: '∞' | number | undefined, format?: string) {
18
11
  if (!number && number !== 0) {
19
12
  return ''
20
13
  }
@@ -8,7 +8,7 @@
8
8
  // Custom icons
9
9
  // -> ./assets/icons
10
10
 
11
- const icons = {
11
+ export const layerIcons = {
12
12
  arrowDown: 'solar:alt-arrow-down-bold',
13
13
  arrowRight: 'solar:alt-arrow-right-bold',
14
14
  arrowUp: 'solar:alt-arrow-up-bold',
@@ -28,19 +28,12 @@ const icons = {
28
28
  tags: 'hugeicons:tags',
29
29
  } as const
30
30
 
31
- export type LayerIconIcon = keyof typeof icons
31
+ export type LayerIconIcon = keyof typeof layerIcons
32
32
 
33
- export type LayerIconValue = typeof icons[LayerIconIcon]
33
+ export type LayerIconValue = typeof layerIcons[LayerIconIcon]
34
34
 
35
- export default function useicons() {
36
- const iconsNames = Object.keys(icons)
35
+ export const layerIconNames = Object.keys(layerIcons) as LayerIconIcon[]
37
36
 
38
- const getIcon = (attribute: LayerIconIcon) => {
39
- return icons[attribute] || icons.default
40
- }
41
-
42
- return {
43
- getIcon,
44
- iconsNames,
45
- }
37
+ export function getLayerIcon(attribute: LayerIconIcon): LayerIconValue {
38
+ return layerIcons[attribute] || layerIcons.default
46
39
  }
package/nuxt.config.ts CHANGED
@@ -6,6 +6,7 @@ import uno from './uno.config.js'
6
6
 
7
7
  const currentDir = path.dirname(fileURLToPath(import.meta.url))
8
8
  const isPostHogEnabled = process.env.NODE_ENV === 'production' && Boolean(process.env.NUXT_PUBLIC_POSTHOG_KEY)
9
+ const isSentryEnabled = process.env.NODE_ENV === 'production'
9
10
 
10
11
  export default defineNuxtConfig({
11
12
  modules: [
@@ -23,6 +24,7 @@ export default defineNuxtConfig({
23
24
  '@vueuse/nuxt',
24
25
  'floating-vue/nuxt',
25
26
  'motion-v/nuxt',
27
+ ...(isSentryEnabled ? ['@sentry/nuxt/module'] : []),
26
28
  ],
27
29
 
28
30
  components: [
@@ -114,6 +116,8 @@ export default defineNuxtConfig({
114
116
 
115
117
  robots: { credits: false },
116
118
 
119
+ sourcemap: { client: 'hidden' },
120
+
117
121
  sitemap: {
118
122
  autoLastmod: true,
119
123
  credits: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",
@@ -31,6 +31,7 @@
31
31
  "@nuxtjs/sitemap": "8.2.0",
32
32
  "@pinia/nuxt": "0.11.3",
33
33
  "@posthog/nuxt": "1.7.76",
34
+ "@sentry/nuxt": "10.57.0",
34
35
  "@unhead/vue": "2.0.19",
35
36
  "@unocss/nuxt": "66.7.0",
36
37
  "@unocss/reset": "66.7.0",
@@ -53,8 +54,8 @@
53
54
  "devDependencies": {
54
55
  "nuxt": "4.3.1",
55
56
  "typescript": "5.9.3",
56
- "@saasmakers/shared": "0.2.8",
57
- "@saasmakers/apps-helpers": "0.1.0"
57
+ "@saasmakers/apps-helpers": "0.1.0",
58
+ "@saasmakers/shared": "0.2.8"
58
59
  },
59
60
  "peerDependencies": {
60
61
  "@saasmakers/apps-helpers": "^0.1.0",
@@ -1,130 +0,0 @@
1
- import type { AreaDrawEvent, BarChartOptions, BarDrawEvent, BaseChart, DrawEvent, LineDrawEvent, Options } from 'chartist'
2
- import { easings } from 'chartist'
3
-
4
- export default function useChartist() {
5
- type EasingType = keyof typeof easings | number[]
6
-
7
- const progressiveLinePlugin = (params: {
8
- animateArea?: boolean
9
- delay?: number
10
- duration?: number
11
- easing?: EasingType
12
- stagger?: number
13
- } = {}) => {
14
- const animateArea = params.animateArea ?? true
15
- const delay = params.delay ?? 0
16
- const duration = params.duration ?? 500
17
- const easing = params.easing ?? easings.easeOutQuart
18
- const stagger = params.stagger ?? 0
19
-
20
- return (chart: BaseChart) => {
21
- chart.on('draw', (ctx: DrawEvent) => {
22
- const begin = delay + (stagger ? stagger * (ctx.seriesIndex ?? 0) : 0)
23
-
24
- // Animation for line charts
25
- if (ctx.type === 'line') {
26
- const lineCtx = ctx as LineDrawEvent
27
- const node = lineCtx.element.getNode<SVGPathElement>()
28
- const length = node.getTotalLength()
29
-
30
- // Set the stroke dasharray and dashoffset
31
- lineCtx.element.attr({
32
- 'stroke-dasharray': `${length}px ${length}px`,
33
- 'stroke-dashoffset': `${length}px`,
34
- })
35
-
36
- // Animate the stroke dashoffset to 0
37
- lineCtx.element.animate({
38
- 'stroke-dashoffset': {
39
- begin,
40
- dur: duration,
41
- easing,
42
- fill: 'freeze',
43
- from: `${length}px`,
44
- to: '0px',
45
- },
46
- }, false)
47
-
48
- // Clean up the dash attributes after it finishes
49
- const total = delay + duration + (stagger ? stagger * (lineCtx.seriesIndex ?? 0) : 0)
50
-
51
- globalThis.setTimeout(() => {
52
- lineCtx.element.attr({
53
- 'stroke-dasharray': undefined,
54
- 'stroke-dashoffset': undefined,
55
- })
56
- }, total + 30)
57
- }
58
-
59
- // Animation for area charts
60
- if (animateArea && ctx.type === 'area') {
61
- const areaCtx = ctx as AreaDrawEvent
62
-
63
- areaCtx.element.animate({
64
-
65
- d: {
66
- begin,
67
- dur: duration,
68
- easing,
69
- fill: 'freeze',
70
- from: areaCtx.path
71
- .clone()
72
- .scale(1, 0)
73
- .translate(0, areaCtx.chartRect.height())
74
- .stringify(),
75
- to: areaCtx.path.stringify(),
76
- },
77
- }, false)
78
- }
79
-
80
- // Animation for bar charts
81
- if (ctx.type === 'bar') {
82
- const barCtx = ctx as BarDrawEvent
83
-
84
- // Access private options property via type assertion
85
- const chartOptions = (chart as unknown as {
86
- options?: Options & Partial<BarChartOptions>
87
- }).options
88
-
89
- const horizontal = !!(chartOptions as BarChartOptions | undefined)?.horizontalBars
90
-
91
- // For vertical bars, y1 is baseline, y2 is top. For horizontal, x1 is baseline, x2 is end.
92
- const from = horizontal ? barCtx.x1 : barCtx.y1
93
- const to = horizontal ? barCtx.x2 : barCtx.y2
94
-
95
- // Start collapsed at baseline
96
- if (horizontal) {
97
- barCtx.element.attr({ x2: from })
98
-
99
- barCtx.element.animate({
100
- x2: {
101
- begin,
102
- dur: duration,
103
- easing,
104
- fill: 'freeze',
105
- from: String(from),
106
- to: String(to),
107
- },
108
- }, false)
109
- }
110
- else {
111
- barCtx.element.attr({ y2: from })
112
-
113
- barCtx.element.animate({
114
- y2: {
115
- begin,
116
- dur: duration,
117
- easing,
118
- fill: 'freeze',
119
- from: String(from),
120
- to: String(to),
121
- },
122
- }, false)
123
- }
124
- }
125
- })
126
- }
127
- }
128
-
129
- return { progressiveLinePlugin }
130
- }
@@ -1,39 +0,0 @@
1
- export default function useMotion() {
2
- const fadeIn = {
3
- animate: {
4
- opacity: 1,
5
- transition: { duration: 0.25 },
6
- },
7
- initial: { opacity: 0 },
8
- }
9
-
10
- const fadeInLeft = {
11
- animate: {
12
- opacity: 1,
13
- transition: { duration: 0.25 },
14
- x: 0,
15
- },
16
- initial: {
17
- opacity: 0,
18
- x: -25,
19
- },
20
- }
21
-
22
- const fadeInUp = {
23
- animate: {
24
- opacity: 1,
25
- transition: { duration: 0.25 },
26
- y: 0,
27
- },
28
- initial: {
29
- opacity: 0,
30
- y: 25,
31
- },
32
- }
33
-
34
- return {
35
- fadeIn,
36
- fadeInLeft,
37
- fadeInUp,
38
- }
39
- }