@svelte-atoms/core 1.0.0-alpha.29 → 1.0.0-alpha.30

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.
Files changed (92) hide show
  1. package/README.md +852 -852
  2. package/dist/attachments/clickout.svelte.d.ts +1 -1
  3. package/dist/attachments/clickout.svelte.js +2 -2
  4. package/dist/components/accordion/accordion-root.svelte +61 -61
  5. package/dist/components/accordion/accordion-root.svelte.d.ts +1 -1
  6. package/dist/components/accordion/accordion.stories.svelte +145 -134
  7. package/dist/components/alert/alert.stories.svelte +400 -401
  8. package/dist/components/atom/html-atom.svelte +71 -17
  9. package/dist/components/avatar/avatar.stories.svelte +22 -27
  10. package/dist/components/badge/badge.stories.svelte +12 -17
  11. package/dist/components/badge/badge.svelte +19 -19
  12. package/dist/components/breadcrumb/breadcrumb.stories.svelte +16 -21
  13. package/dist/components/button/button.stories.svelte +27 -60
  14. package/dist/components/calendar/calendar-day.svelte +96 -96
  15. package/dist/components/calendar/calendar-header.svelte +29 -29
  16. package/dist/components/calendar/calendar-root.svelte +206 -206
  17. package/dist/components/calendar/calendar.stories.svelte +10 -15
  18. package/dist/components/card/card-body.svelte +39 -39
  19. package/dist/components/card/card-footer.svelte +41 -41
  20. package/dist/components/card/card-root.svelte +91 -91
  21. package/dist/components/card/card.stories.svelte +133 -145
  22. package/dist/components/checkbox/checkbox.stories.svelte +22 -27
  23. package/dist/components/checkbox/checkbox.svelte +155 -157
  24. package/dist/components/collapsible/collapsible.stories.svelte +172 -173
  25. package/dist/components/combobox/combobox-root.svelte +65 -65
  26. package/dist/components/combobox/compobox.stories.svelte +51 -54
  27. package/dist/components/container/container.stories.svelte +20 -23
  28. package/dist/components/datagrid/datagrid-root.svelte +59 -59
  29. package/dist/components/datagrid/datagrid.css +5 -5
  30. package/dist/components/datagrid/datagrid.stories.svelte +72 -75
  31. package/dist/components/date-picker/date-picker-calendar.svelte +67 -67
  32. package/dist/components/date-picker/date-picker-header.svelte +100 -100
  33. package/dist/components/date-picker/date-picker-months.svelte +142 -142
  34. package/dist/components/date-picker/date-picker-root.svelte +1 -1
  35. package/dist/components/date-picker/date-picker-years.svelte +205 -205
  36. package/dist/components/date-picker/date-picker.stories.svelte +11 -18
  37. package/dist/components/dialog/dialog-content.svelte +62 -62
  38. package/dist/components/dialog/dialog.stories.svelte +64 -67
  39. package/dist/components/drawer/attachments.svelte.js +8 -9
  40. package/dist/components/drawer/drawer-content.svelte +57 -42
  41. package/dist/components/drawer/drawer.stories.svelte +212 -224
  42. package/dist/components/dropdown/dropdown-root.svelte +59 -59
  43. package/dist/components/dropdown/dropdown.stories.svelte +80 -83
  44. package/dist/components/form/form.stories.svelte +96 -99
  45. package/dist/components/image/image.stories.svelte +20 -23
  46. package/dist/components/input/input.stories.svelte +35 -38
  47. package/dist/components/label/label.stories.svelte +15 -26
  48. package/dist/components/label/label.stories.svelte.d.ts +24 -4
  49. package/dist/components/lazy/lazy.stories.svelte +9 -16
  50. package/dist/components/lazy/lazy.svelte +28 -28
  51. package/dist/components/link/link.stories.svelte +15 -26
  52. package/dist/components/link/link.stories.svelte.d.ts +24 -4
  53. package/dist/components/menu/menu-list.svelte +40 -40
  54. package/dist/components/menu/menu.stories.svelte +33 -36
  55. package/dist/components/popover/bond.svelte.js +31 -25
  56. package/dist/components/popover/popover-arrow.svelte +111 -111
  57. package/dist/components/popover/popover-content.svelte +175 -178
  58. package/dist/components/popover/popover-indicator.svelte +44 -43
  59. package/dist/components/popover/popover-root.svelte +48 -48
  60. package/dist/components/popover/popover.stories.svelte +49 -52
  61. package/dist/components/qr-code/qr-code.stories.svelte +4 -13
  62. package/dist/components/qr-code/qr-code.svelte +75 -75
  63. package/dist/components/radio/radio-group.stories.svelte +41 -50
  64. package/dist/components/radio/radio.stories.svelte +17 -26
  65. package/dist/components/radio/radio.svelte +109 -109
  66. package/dist/components/root/root.svelte +121 -121
  67. package/dist/components/root/root.svelte.d.ts +1 -1
  68. package/dist/components/scrollable/scrollable.stories.svelte +116 -126
  69. package/dist/components/sidebar/sidebar-content.svelte +13 -2
  70. package/dist/components/sidebar/sidebar-root.svelte +10 -12
  71. package/dist/components/sidebar/sidebar.stories.svelte +8 -19
  72. package/dist/components/sidebar/types.d.ts +1 -0
  73. package/dist/components/tabs/tab/bond.svelte.d.ts +4 -1
  74. package/dist/components/tabs/tab/bond.svelte.js +4 -1
  75. package/dist/components/tabs/tabs.stories.svelte +56 -59
  76. package/dist/components/tooltip/tooltip-trigger.svelte +39 -37
  77. package/dist/components/tooltip/tooltip.stories.svelte +32 -35
  78. package/dist/components/tree/tree.stories.svelte +142 -134
  79. package/dist/context/preset.svelte.d.ts +3 -3
  80. package/dist/utils/function.d.ts +2 -0
  81. package/dist/utils/function.js +6 -0
  82. package/package.json +6 -9
  83. package/dist/actions/animation.svelte.d.ts +0 -6
  84. package/dist/actions/animation.svelte.js +0 -14
  85. package/dist/actions/clickout.svelte.d.ts +0 -2
  86. package/dist/actions/clickout.svelte.js +0 -15
  87. package/dist/actions/popover.svelte.d.ts +0 -19
  88. package/dist/actions/popover.svelte.js +0 -81
  89. package/dist/actions/portal.svelte.d.ts +0 -8
  90. package/dist/actions/portal.svelte.js +0 -32
  91. package/dist/attachments/gsap.svelte.d.ts +0 -2
  92. package/dist/attachments/gsap.svelte.js +0 -26
@@ -9,6 +9,7 @@
9
9
  import type { Bond } from '../../shared';
10
10
  import SnippetRenderer from './snippet-renderer.svelte';
11
11
  import type { Component } from 'svelte';
12
+ import { call } from '../../utils/function';
12
13
 
13
14
  type Element = HTMLElementTagNameMap[E];
14
15
 
@@ -25,9 +26,20 @@
25
26
  ...restProps
26
27
  }: HtmlAtomProps<E, B> & Omit<HTMLAttributes<Element>, 'children'> = $props();
27
28
 
28
- const preset = $derived(
29
- presetKey ? getPreset(presetKey as PresetModuleName)?.apply?.(bond, [bond]) : undefined
30
- );
29
+ /**
30
+ * Resolve variant definition to props
31
+ */
32
+ // Cache for resolved variants to avoid recomputation
33
+ // Key: JSON stringified combination of variant props
34
+ const variantCache = new Map<string, Record<string, any>>();
35
+
36
+ // Memoize preset resolution - only recompute when presetKey or bond changes
37
+ const preset = $derived.by(() => {
38
+ if (!presetKey) return undefined;
39
+ const result = getPreset(presetKey as PresetModuleName)?.apply?.(bond, [bond]);
40
+ // Handle deferred preset result (factory function)
41
+ return call(result);
42
+ });
31
43
 
32
44
  const presetProps = $derived(preset?.variants);
33
45
 
@@ -45,6 +57,7 @@
45
57
  });
46
58
 
47
59
  // Merge preset variants with local variants
60
+ // Memoized to avoid recomputation when inputs haven't changed
48
61
  const mergedVariants = $derived.by(() => {
49
62
  // No variants at all
50
63
  if (!presetProps && !localVariants) return undefined;
@@ -52,8 +65,8 @@
52
65
  // Only preset variants (raw object from preset)
53
66
  if (presetProps && !localVariants) {
54
67
  // Convert preset variants to VariantDefinition-like structure
55
- const variantDef = {
56
- class: preset?.class,
68
+ const variantDef: VariantDefinition<any> = {
69
+ class: preset?.class ?? '',
57
70
  variants: presetProps,
58
71
  compounds: preset?.compounds ?? [],
59
72
  defaults: preset?.defaults ?? {}
@@ -68,9 +81,9 @@
68
81
 
69
82
  // Both exist - merge them
70
83
  // When both preset and local variants exist, we need to merge the resolved props
71
- const presetVariantDef = {
72
- class: preset?.class,
73
- variants: presetProps,
84
+ const presetVariantDef: VariantDefinition<any> = {
85
+ class: preset?.class ?? '',
86
+ variants: presetProps ?? {},
74
87
  compounds: preset?.compounds ?? [],
75
88
  defaults: preset?.defaults ?? {}
76
89
  };
@@ -93,9 +106,31 @@
93
106
  };
94
107
  });
95
108
 
96
- const _klass = $derived(
97
- cn(klass, mergedVariants?.class ?? '').replaceAll('$preset', cn(preset?.class))
98
- );
109
+ const presetClassString = $derived(cn(preset?.class));
110
+
111
+ const _klass = $derived.by(() => {
112
+ const klassStr = cn(klass ?? '');
113
+ // Check for $preset placeholder first
114
+ if (!klassStr.includes('$preset')) {
115
+ // No placeholder - normal merge: variants override direct class
116
+ return cn(klass, mergedVariants?.class ?? '');
117
+ }
118
+
119
+ // Has placeholder - calculate position and inject preset classes
120
+ const parts = klassStr.split('$preset');
121
+
122
+ // Only keep the last $preset placeholder
123
+ const beforeLastPlaceholder = parts.slice(0, -1).join('');
124
+ const afterLastPlaceholder = parts[parts.length - 1];
125
+
126
+ // Merge: before + preset + variants + after
127
+ return cn(
128
+ beforeLastPlaceholder,
129
+ presetClassString,
130
+ mergedVariants?.class ?? '',
131
+ afterLastPlaceholder
132
+ );
133
+ });
99
134
 
100
135
  const _base = $derived(base ?? preset?.base);
101
136
  const _as = $derived(as ?? preset?.as);
@@ -131,9 +166,6 @@
131
166
  };
132
167
  }) as { component: Component; props: Record<string, any> };
133
168
 
134
- /**
135
- * Resolve variant definition to props
136
- */
137
169
  function resolveVariants(
138
170
  def: VariantDefinition<any>,
139
171
  bond: Bond | null | undefined,
@@ -144,6 +176,18 @@
144
176
  // Merge props with defaults
145
177
  const finalProps = { ...defaults, ...props };
146
178
 
179
+ // Create cache key from final props (only variant-related props)
180
+ const variantKeys = variantMap ? Object.keys(variantMap) : [];
181
+ const relevantProps = Object.fromEntries(
182
+ Object.entries(finalProps).filter(([key]) => variantKeys.includes(key))
183
+ );
184
+ const cacheKey = JSON.stringify({ relevantProps, baseClass, compounds });
185
+
186
+ // Check cache
187
+ if (variantCache.has(cacheKey)) {
188
+ return variantCache.get(cacheKey)!;
189
+ }
190
+
147
191
  const classes: ClassValue[] = [];
148
192
  const attributes: Record<string, any> = {};
149
193
 
@@ -193,15 +237,25 @@
193
237
  }
194
238
  }
195
239
 
196
- return {
240
+ const result = {
197
241
  class: classes,
198
242
  ...attributes
199
243
  };
244
+
245
+ // Store in cache (limit cache size to prevent memory leaks)
246
+ if (variantCache.size > 100) {
247
+ // Clear oldest entry (first in Map)
248
+ const firstKey = variantCache.keys().next().value;
249
+ if (firstKey) variantCache.delete(firstKey);
250
+ }
251
+ variantCache.set(cacheKey, result);
252
+
253
+ return result;
200
254
  }
201
255
  </script>
202
256
 
203
257
  <renderer.component {...renderer.props}>
204
- {#snippet children(args)}
205
- {@render childrenProp?.(args)}
258
+ {#snippet children(args: any)}
259
+ {@render (childrenProp as any)?.(args)}
206
260
  {/snippet}
207
261
  </renderer.component>
@@ -1,27 +1,22 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import AvatarCmp from './avatar.svelte';
4
- import Root from '../root/root.svelte';
5
- import CalendarRegularIcon from '../../icons/icon-arrow-down.svelte';
6
-
7
- const { Story } = defineMeta({
8
- title: 'ATOMS/Avatar'
9
- });
10
- </script>
11
-
12
- <Story name="Avatar">
13
- <Root>
14
- {#snippet children({ args })}
15
- <div class="flex h-full w-full items-center justify-center gap-4">
16
- <AvatarCmp alt="Abdelhalim Riache" />
17
-
18
- <AvatarCmp src={CalendarRegularIcon} alt="Abdelhalim Riache" />
19
-
20
- <AvatarCmp
21
- src="https://sevennaturalwonders.org/wp-content/uploads/2023/12/1-Mount-Fuji.jpg"
22
- alt="Mount Fuji"
23
- />
24
- </div>
25
- {/snippet}
26
- </Root>
27
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import AvatarCmp from './avatar.svelte';
4
+ import CalendarRegularIcon from '../../icons/icon-arrow-down.svelte';
5
+
6
+ const { Story } = defineMeta({
7
+ title: 'ATOMS/Avatar'
8
+ });
9
+ </script>
10
+
11
+ <Story name="Avatar">
12
+ <div class="flex h-full w-full items-center justify-center gap-4">
13
+ <AvatarCmp alt="Abdelhalim Riache" />
14
+
15
+ <AvatarCmp src={CalendarRegularIcon} alt="Abdelhalim Riache" />
16
+
17
+ <AvatarCmp
18
+ src="https://sevennaturalwonders.org/wp-content/uploads/2023/12/1-Mount-Fuji.jpg"
19
+ alt="Mount Fuji"
20
+ />
21
+ </div>
22
+ </Story>
@@ -1,17 +1,12 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import Root from '../root/root.svelte';
4
- import { Badge as BadgeModule } from '.';
5
-
6
- const { Story } = defineMeta({
7
- title: 'ATOMS/Badge'
8
- });
9
- </script>
10
-
11
- <Story name="Badge">
12
- <Root>
13
- {#snippet children({ args })}
14
- <BadgeModule>Badge</BadgeModule>
15
- {/snippet}
16
- </Root>
17
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { Badge as BadgeModule } from '.';
4
+
5
+ const { Story } = defineMeta({
6
+ title: 'ATOMS/Badge'
7
+ });
8
+ </script>
9
+
10
+ <Story name="Badge">
11
+ <BadgeModule>Badge</BadgeModule>
12
+ </Story>
@@ -1,19 +1,19 @@
1
- <script lang="ts">
2
- import { HtmlAtom } from '../atom';
3
- import type { BadgeProps } from './types';
4
-
5
- let { class: klass = '', as = 'span', children = undefined, ...restProps }: BadgeProps = $props();
6
- </script>
7
-
8
- <HtmlAtom
9
- preset="badge"
10
- class={[
11
- 'bg-foreground/10 border-border text-foreground inline-flex h-auto items-center rounded-full px-2.5 py-0.5 text-xs font-medium',
12
- '$preset',
13
- klass
14
- ]}
15
- {as}
16
- {...restProps}
17
- >
18
- {@render children?.()}
19
- </HtmlAtom>
1
+ <script lang="ts">
2
+ import { HtmlAtom } from '../atom';
3
+ import type { BadgeProps } from './types';
4
+
5
+ let { class: klass = '', as = 'span', children = undefined, ...restProps }: BadgeProps = $props();
6
+ </script>
7
+
8
+ <HtmlAtom
9
+ preset="badge"
10
+ class={[
11
+ 'bg-foreground/10 border-border text-foreground inline-flex h-auto w-fit items-center rounded-full px-2.5 py-0.5 text-xs font-medium',
12
+ '$preset',
13
+ klass
14
+ ]}
15
+ {as}
16
+ {...restProps}
17
+ >
18
+ {@render children?.()}
19
+ </HtmlAtom>
@@ -1,21 +1,16 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import Root from '../root/root.svelte';
4
- import { Breadcrumb as BreadcrumbModule } from '.';
5
-
6
- const { Story } = defineMeta({
7
- title: 'ATOMS/Breadcrumb'
8
- });
9
- </script>
10
-
11
- <Story name="Breadcrumb">
12
- <Root>
13
- {#snippet children({ args })}
14
- <BreadcrumbModule.Root>
15
- <BreadcrumbModule.Item href="/">Home</BreadcrumbModule.Item>
16
- <BreadcrumbModule.Separator>/</BreadcrumbModule.Separator>
17
- <BreadcrumbModule.Item href="/vehicles">Vehicles</BreadcrumbModule.Item>
18
- </BreadcrumbModule.Root>
19
- {/snippet}
20
- </Root>
21
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { Breadcrumb as BreadcrumbModule } from '.';
4
+
5
+ const { Story } = defineMeta({
6
+ title: 'ATOMS/Breadcrumb'
7
+ });
8
+ </script>
9
+
10
+ <Story name="Breadcrumb">
11
+ <BreadcrumbModule.Root>
12
+ <BreadcrumbModule.Item href="/">Home</BreadcrumbModule.Item>
13
+ <BreadcrumbModule.Separator>/</BreadcrumbModule.Separator>
14
+ <BreadcrumbModule.Item href="/vehicles">Vehicles</BreadcrumbModule.Item>
15
+ </BreadcrumbModule.Root>
16
+ </Story>
@@ -1,60 +1,27 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import Root from '../root/root.svelte';
4
- import ButtonCmp from './button.svelte';
5
- import { defineVariants } from '../../utils/variant';
6
-
7
- const { Story } = defineMeta({
8
- title: 'ATOMS/Button',
9
- argTypes: {
10
- variant: {
11
- control: 'select',
12
- options: ['primary', 'secondary', 'destructive', 'outline', 'ghost'],
13
- description: 'Button variant style',
14
- table: {
15
- defaultValue: { summary: 'primary' }
16
- }
17
- }
18
- }
19
- });
20
- </script>
21
-
22
- <script lang="ts">
23
- const variants = defineVariants((bond) => ({
24
- variants: {
25
- variant: {
26
- primary: {
27
- class: 'bg-primary text-primary-foreground hover:bg-primary/80 active:bg-primary/90'
28
- },
29
- secondary: {
30
- class:
31
- 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/90'
32
- },
33
- destructive: {
34
- class:
35
- 'bg-destructive text-destructive-foreground hover:bg-destructive/80 active:bg-destructive/90'
36
- },
37
- outline: {
38
- class:
39
- 'bg-transparent hover:bg-foreground/5 active:bg-foreground/10 border border-border text-foreground'
40
- },
41
- ghost: {
42
- class:
43
- 'bg-transparent text-foreground hover:bg-foreground/5 active:bg-foreground/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'
44
- }
45
- }
46
- },
47
- compounds: [],
48
- defaults: {
49
- variant: 'destructive'
50
- }
51
- }));
52
- </script>
53
-
54
- <Story name="Button">
55
- {#snippet template(args)}
56
- <Root class="p-4">
57
- <ButtonCmp {variants} {...args}>Clicke me</ButtonCmp>
58
- </Root>
59
- {/snippet}
60
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import ButtonCmp from './button.svelte';
4
+
5
+ const { Story } = defineMeta({
6
+ title: 'ATOMS/Button',
7
+ argTypes: {
8
+ variant: {
9
+ control: 'select',
10
+ options: ['primary', 'secondary', 'destructive', 'outline', 'ghost'],
11
+ description: 'Button variant style',
12
+ table: {
13
+ defaultValue: { summary: 'primary' }
14
+ }
15
+ }
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ </script>
22
+
23
+ <Story name="Button">
24
+ {#snippet template(args)}
25
+ <ButtonCmp {...args}>Click me</ButtonCmp>
26
+ {/snippet}
27
+ </Story>
@@ -1,96 +1,96 @@
1
- <script lang="ts">
2
- import { isBefore, isSameDay, isWithinInterval } from 'date-fns';
3
- import { CalendarBond } from './bond.svelte';
4
- import type { CalendarDayProps } from './types';
5
- import { HtmlAtom } from '../atom';
6
-
7
- const calendarBond = CalendarBond.get();
8
-
9
- const selectedDateStart = $derived(calendarBond?.state.props.start);
10
- const selectedDateEnd = $derived(calendarBond?.state.props.end);
11
- const isRange = $derived(Array.isArray(calendarBond?.state.props.type === 'range'));
12
-
13
- let {
14
- class: klass = '',
15
- preset = 'calendar.day',
16
- day,
17
- as = 'button',
18
- children = undefined,
19
- onclick = handleClick,
20
- ...restProps
21
- }: CalendarDayProps = $props();
22
-
23
- const dayProps = $derived({
24
- ...calendarBond?.day(day),
25
- ...restProps
26
- });
27
-
28
- const isSelected = $derived.by(() => {
29
- if (selectedDateEnd && selectedDateStart) {
30
- return isWithinInterval(day.date, { end: selectedDateEnd, start: selectedDateStart });
31
- }
32
-
33
- return selectedDateStart && isSameDay(day.date, selectedDateStart);
34
- });
35
-
36
- function handleClick() {
37
- if (isRange) {
38
- const start = calendarBond?.state.props.start;
39
- if (!start) {
40
- calendarBond?.state.selectStart(new Date(day.date));
41
- return;
42
- }
43
-
44
- if (isBefore(new Date(day.date), new Date(start))) {
45
- calendarBond?.state.selectStart(new Date(day.date));
46
- return;
47
- }
48
-
49
- calendarBond?.state.selectEnd(new Date(day.date));
50
- } else {
51
- calendarBond?.state.selectStart(new Date(day.date));
52
- }
53
- }
54
- </script>
55
-
56
- <HtmlAtom
57
- {as}
58
- {preset}
59
- class={[
60
- 'calendar-day text-foreground border-border hover:bg-accent hover:text-accent-foreground h-12 cursor-pointer border-b border-l p-1 transition-colors',
61
- day.offmonth && 'text-muted-foreground bg-muted/30',
62
- day.weekend && 'bg-muted/50',
63
- isSelected &&
64
- 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground',
65
- isSelected && day.offmonth && 'bg-primary/70',
66
- day.today && !isSelected && 'border-primary border-2 font-semibold',
67
- day.disabled && 'pointer-events-none opacity-50',
68
- klass
69
- ]}
70
- data-disabled={day.disabled}
71
- data-prec={day.fromPreviousMonth}
72
- data-next={day.fromNextMonth}
73
- data-offmonth={day.offmonth}
74
- data-weekend={day.weekend}
75
- data-today={day.today}
76
- data-selected={isSelected}
77
- {onclick}
78
- {...dayProps}
79
- >
80
- {#if children}
81
- {@render children({
82
- calendar: calendarBond!
83
- })}
84
- {:else}
85
- <span class="value">{day.dayOfMonth}</span>
86
- {/if}
87
- </HtmlAtom>
88
-
89
- <style>
90
- :global(.calendar-day):nth-child(7n + 1) {
91
- border-left: none;
92
- }
93
- :global(.calendar-day):nth-last-child(-n + 7) {
94
- border-bottom: none;
95
- }
96
- </style>
1
+ <script lang="ts">
2
+ import { isBefore, isSameDay, isWithinInterval } from 'date-fns';
3
+ import { CalendarBond } from './bond.svelte';
4
+ import type { CalendarDayProps } from './types';
5
+ import { HtmlAtom } from '../atom';
6
+
7
+ const calendarBond = CalendarBond.get();
8
+
9
+ const selectedDateStart = $derived(calendarBond?.state.props.start);
10
+ const selectedDateEnd = $derived(calendarBond?.state.props.end);
11
+ const isRange = $derived(Array.isArray(calendarBond?.state.props.type === 'range'));
12
+
13
+ let {
14
+ class: klass = '',
15
+ preset = 'calendar.day',
16
+ day,
17
+ as = 'button',
18
+ children = undefined,
19
+ onclick = handleClick,
20
+ ...restProps
21
+ }: CalendarDayProps = $props();
22
+
23
+ const dayProps = $derived({
24
+ ...calendarBond?.day(day),
25
+ ...restProps
26
+ });
27
+
28
+ const isSelected = $derived.by(() => {
29
+ if (selectedDateEnd && selectedDateStart) {
30
+ return isWithinInterval(day.date, { end: selectedDateEnd, start: selectedDateStart });
31
+ }
32
+
33
+ return selectedDateStart && isSameDay(day.date, selectedDateStart);
34
+ });
35
+
36
+ function handleClick() {
37
+ if (isRange) {
38
+ const start = calendarBond?.state.props.start;
39
+ if (!start) {
40
+ calendarBond?.state.selectStart(new Date(day.date));
41
+ return;
42
+ }
43
+
44
+ if (isBefore(new Date(day.date), new Date(start))) {
45
+ calendarBond?.state.selectStart(new Date(day.date));
46
+ return;
47
+ }
48
+
49
+ calendarBond?.state.selectEnd(new Date(day.date));
50
+ } else {
51
+ calendarBond?.state.selectStart(new Date(day.date));
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <HtmlAtom
57
+ {as}
58
+ {preset}
59
+ class={[
60
+ 'calendar-day text-foreground border-border hover:bg-accent hover:text-accent-foreground h-12 cursor-pointer border-b border-l p-1 transition-colors',
61
+ day.offmonth && 'text-muted-foreground bg-muted/30',
62
+ day.weekend && 'bg-muted/50',
63
+ isSelected &&
64
+ 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground',
65
+ isSelected && day.offmonth && 'bg-primary/70',
66
+ day.today && !isSelected && 'border-primary border-2 font-semibold',
67
+ day.disabled && 'pointer-events-none opacity-50',
68
+ klass
69
+ ]}
70
+ data-disabled={day.disabled}
71
+ data-prec={day.fromPreviousMonth}
72
+ data-next={day.fromNextMonth}
73
+ data-offmonth={day.offmonth}
74
+ data-weekend={day.weekend}
75
+ data-today={day.today}
76
+ data-selected={isSelected}
77
+ {onclick}
78
+ {...dayProps}
79
+ >
80
+ {#if children}
81
+ {@render children({
82
+ calendar: calendarBond!
83
+ })}
84
+ {:else}
85
+ <span class="value">{day.dayOfMonth}</span>
86
+ {/if}
87
+ </HtmlAtom>
88
+
89
+ <style>
90
+ :global(.calendar-day):nth-child(7n + 1) {
91
+ border-left: none;
92
+ }
93
+ :global(.calendar-day):nth-last-child(-n + 7) {
94
+ border-bottom: none;
95
+ }
96
+ </style>
@@ -1,29 +1,29 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils';
3
- import { HtmlAtom } from '../atom';
4
- import { CalendarBond } from './bond.svelte';
5
- import CalendarWeekDay from './calendar-week-day.svelte';
6
-
7
- const calendarBond = CalendarBond.get();
8
- const currentMonth = $derived(calendarBond?.state.props.currentMonth);
9
-
10
- let { class: klass = '', preset = 'calendar.header', ...restProps } = $props();
11
-
12
- const headerProps = $derived({
13
- ...calendarBond?.header(),
14
- ...restProps
15
- });
16
- </script>
17
-
18
- <HtmlAtom
19
- {preset}
20
- class={cn(
21
- 'calendar-header border-border col-span-full grid h-fit grid-cols-subgrid border-b',
22
- klass
23
- )}
24
- {...headerProps}
25
- >
26
- {#each (currentMonth?.days ?? []).filter((d) => d.week == 1) as day}
27
- <CalendarWeekDay isWeekend={day.weekend}>{day.name}</CalendarWeekDay>
28
- {/each}
29
- </HtmlAtom>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils';
3
+ import { HtmlAtom } from '../atom';
4
+ import { CalendarBond } from './bond.svelte';
5
+ import CalendarWeekDay from './calendar-week-day.svelte';
6
+
7
+ const calendarBond = CalendarBond.get();
8
+ const currentMonth = $derived(calendarBond?.state.props.currentMonth);
9
+
10
+ let { class: klass = '', preset = 'calendar.header', ...restProps } = $props();
11
+
12
+ const headerProps = $derived({
13
+ ...calendarBond?.header(),
14
+ ...restProps
15
+ });
16
+ </script>
17
+
18
+ <HtmlAtom
19
+ {preset}
20
+ class={cn(
21
+ 'calendar-header border-border col-span-full grid h-fit grid-cols-subgrid border-b',
22
+ klass
23
+ )}
24
+ {...headerProps}
25
+ >
26
+ {#each (currentMonth?.days ?? []).filter((d) => d.week == 1) as day}
27
+ <CalendarWeekDay isWeekend={day.weekend}>{day.name}</CalendarWeekDay>
28
+ {/each}
29
+ </HtmlAtom>