@noxlovette/material 0.4.7 → 0.5.0

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 (134) hide show
  1. package/dist/animation/TransitionPatterns.stories.svelte +357 -0
  2. package/dist/animation/TransitionPatterns.stories.svelte.d.ts +3 -0
  3. package/dist/animation/containerTransform.d.ts +26 -12
  4. package/dist/animation/containerTransform.js +22 -138
  5. package/dist/animation/enterExit.d.ts +21 -3
  6. package/dist/animation/enterExit.js +69 -22
  7. package/dist/animation/index.d.ts +1 -1
  8. package/dist/animation/index.js +1 -1
  9. package/dist/animation/presence.svelte.d.ts +47 -0
  10. package/dist/animation/presence.svelte.js +84 -0
  11. package/dist/animation/sharedAxisTransition.d.ts +35 -37
  12. package/dist/animation/sharedAxisTransition.js +50 -22
  13. package/dist/animation/skeleton.d.ts +11 -0
  14. package/dist/animation/skeleton.js +23 -0
  15. package/dist/animation/spring.d.ts +61 -0
  16. package/dist/animation/spring.js +53 -0
  17. package/dist/animation/springValue.svelte.d.ts +22 -0
  18. package/dist/animation/springValue.svelte.js +42 -0
  19. package/dist/animation/transition.d.ts +3 -1
  20. package/dist/animation/transition.js +3 -1
  21. package/dist/attachments/clickOutside.d.ts +13 -0
  22. package/dist/attachments/clickOutside.js +18 -0
  23. package/dist/attachments/index.d.ts +1 -0
  24. package/dist/attachments/index.js +1 -0
  25. package/dist/components/buttons/FAB.svelte +23 -16
  26. package/dist/components/buttons/FABMenu.svelte +24 -29
  27. package/dist/components/buttons/FABMenu.svelte.d.ts +2 -1
  28. package/dist/components/buttons/FABMenuItem.svelte +23 -20
  29. package/dist/components/buttons/FABMenuItem.svelte.d.ts +5 -1
  30. package/dist/components/buttons/split-button/SplitButton.svelte +10 -17
  31. package/dist/components/buttons/split-button/theme.js +1 -1
  32. package/dist/components/buttons/types.d.ts +3 -3
  33. package/dist/components/cards/theme.js +1 -1
  34. package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +8 -12
  35. package/dist/components/containers/bottom-sheet/BottomSheet.svelte +67 -55
  36. package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +1 -1
  37. package/dist/components/containers/bottom-sheet/types.d.ts +8 -2
  38. package/dist/components/containers/context-menu/ContextMenu.svelte +23 -36
  39. package/dist/components/containers/context-menu/theme.js +2 -2
  40. package/dist/components/containers/dialogue/Dialogue.svelte +49 -65
  41. package/dist/components/containers/link-preview/LinkPreview.svelte +12 -23
  42. package/dist/components/containers/link-preview/theme.d.ts +3 -3
  43. package/dist/components/containers/menu/Menu.svelte +14 -21
  44. package/dist/components/containers/menu/MenuSub.svelte +10 -17
  45. package/dist/components/containers/pane/DraggablePane.svelte +90 -5
  46. package/dist/components/containers/pane/DraggablePane.svelte.d.ts +1 -1
  47. package/dist/components/containers/pane/theme.js +2 -2
  48. package/dist/components/containers/popover/Popover.svelte +31 -42
  49. package/dist/components/containers/popover/theme.d.ts +3 -3
  50. package/dist/components/containers/scroll-area/theme.js +2 -2
  51. package/dist/components/containers/side-sheet/SideSheet.stories.svelte +10 -15
  52. package/dist/components/containers/side-sheet/SideSheet.svelte +44 -44
  53. package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +4 -3
  54. package/dist/components/containers/side-sheet/types.d.ts +8 -2
  55. package/dist/components/date/DateField.svelte +85 -92
  56. package/dist/components/date/DateField.svelte.d.ts +2 -5
  57. package/dist/components/date/DateRangeField.svelte +113 -155
  58. package/dist/components/date/DateRangeField.svelte.d.ts +2 -5
  59. package/dist/components/date/theme.d.ts +95 -0
  60. package/dist/components/date/theme.js +99 -14
  61. package/dist/components/date/types.d.ts +13 -0
  62. package/dist/components/forms/checkbox/theme.js +2 -2
  63. package/dist/components/forms/command/theme.d.ts +3 -3
  64. package/dist/components/forms/command/theme.js +1 -1
  65. package/dist/components/forms/pin/theme.js +2 -2
  66. package/dist/components/forms/radio-group/theme.js +2 -2
  67. package/dist/components/forms/select/Select.svelte +42 -54
  68. package/dist/components/forms/select/theme.js +4 -4
  69. package/dist/components/forms/slider/Slider.svelte +144 -126
  70. package/dist/components/forms/slider/Slider.svelte.d.ts +3 -0
  71. package/dist/components/forms/slider/theme.d.ts +154 -48
  72. package/dist/components/forms/slider/theme.js +60 -20
  73. package/dist/components/forms/slider/types.d.ts +1 -1
  74. package/dist/components/forms/switch/theme.js +4 -4
  75. package/dist/components/forms/textfield/Textfield.svelte +1 -1
  76. package/dist/components/forms/textfield/theme.d.ts +9 -0
  77. package/dist/components/forms/textfield/theme.js +26 -9
  78. package/dist/components/forms/toggle-group/theme.js +1 -1
  79. package/dist/components/forms/tooltip/Tooltip.svelte +24 -35
  80. package/dist/components/nav/breadcrumb/theme.js +1 -1
  81. package/dist/components/nav/navbar/theme.js +2 -2
  82. package/dist/components/nav/rail/Rail.svelte +4 -5
  83. package/dist/components/nav/rail/theme.js +7 -7
  84. package/dist/components/nav/tabs/theme.js +1 -1
  85. package/dist/components/pill/theme.d.ts +1 -1
  86. package/dist/components/pill/theme.js +1 -1
  87. package/dist/components/progress/CircularProgress.svelte +78 -4
  88. package/dist/components/progress/CircularProgress.svelte.d.ts +5 -0
  89. package/dist/components/progress/LinearProgress.svelte +70 -7
  90. package/dist/components/progress/LinearProgress.svelte.d.ts +5 -0
  91. package/dist/components/progress/WavyLinearProgress.svelte +27 -3
  92. package/dist/components/progress/WavyLinearProgress.svelte.d.ts +3 -0
  93. package/dist/components/progress/_wavy.d.ts +2 -0
  94. package/dist/components/progress/_wavy.js +3 -1
  95. package/dist/components/snackbar/Snackbar.svelte +13 -17
  96. package/dist/components/table/Table.stories.svelte +24 -0
  97. package/dist/components/table/TableBody.svelte +3 -16
  98. package/dist/components/table/TableBody.svelte.d.ts +1 -5
  99. package/dist/components/table/TableHeader.svelte +1 -1
  100. package/dist/components/table/theme.js +2 -2
  101. package/dist/components/table/types.d.ts +0 -7
  102. package/dist/components/time/TimeField.stories.svelte +41 -0
  103. package/dist/components/time/TimeField.stories.svelte.d.ts +19 -0
  104. package/dist/components/time/TimeField.svelte +62 -61
  105. package/dist/components/toolbar/theme.js +2 -2
  106. package/dist/index.d.ts +1 -1
  107. package/dist/index.js +1 -1
  108. package/dist/styles/component.css +21 -22
  109. package/dist/styles/motion.css +317 -38
  110. package/dist/utils/Layer.svelte +16 -29
  111. package/dist/utils/icon/LoadingIndicator.svelte +61 -22
  112. package/dist/utils/icon/loadingShapes.d.ts +1 -0
  113. package/dist/utils/icon/loadingShapes.js +15 -0
  114. package/package.json +5 -2
  115. package/dist/actions/clickOutside.d.ts +0 -3
  116. package/dist/actions/clickOutside.js +0 -13
  117. package/dist/actions/floating.d.ts +0 -20
  118. package/dist/actions/floating.js +0 -87
  119. package/dist/actions/index.d.ts +0 -6
  120. package/dist/actions/index.js +0 -6
  121. package/dist/actions/keyboard.d.ts +0 -3
  122. package/dist/actions/keyboard.js +0 -14
  123. package/dist/actions/portal.d.ts +0 -3
  124. package/dist/actions/portal.js +0 -8
  125. package/dist/actions/positionFloating.d.ts +0 -10
  126. package/dist/actions/positionFloating.js +0 -59
  127. package/dist/actions/rovingFocusGroup.d.ts +0 -20
  128. package/dist/actions/rovingFocusGroup.js +0 -82
  129. package/dist/animation/easing.d.ts +0 -6
  130. package/dist/animation/easing.js +0 -78
  131. package/dist/animation/outroClass.d.ts +0 -3
  132. package/dist/animation/outroClass.js +0 -20
  133. package/dist/animation/transitionTypes.d.ts +0 -8
  134. package/dist/animation/transitionTypes.js +0 -1
@@ -5,11 +5,11 @@ Popovers display rich content in a non-modal overlay anchored to a trigger eleme
5
5
  @see https://m3.material.io/components/dialogs/overview (non-modal surface variant)
6
6
  -->
7
7
  <script lang="ts">
8
+ import { enterExit, presence } from '../../../animation/index.js';
8
9
  import { Popover } from 'bits-ui';
9
10
  import clsx from 'clsx';
10
11
  import { popover } from './theme.js';
11
12
  import type { PopoverProps } from './types.js';
12
- import { enterExit, easeEmphasizedDecel, easeEmphasizedAccel } from '../../../animation/index.js';
13
13
  import ButtonIcon from '../../buttons/ButtonIcon.svelte';
14
14
  import Title from '../../typography/title/Title.svelte';
15
15
  let {
@@ -35,49 +35,38 @@ Popovers display rich content in a non-modal overlay anchored to a trigger eleme
35
35
  </Popover.Trigger>
36
36
 
37
37
  <Popover.Portal>
38
- <Popover.Content forceMount {side} {align} {sideOffset}>
38
+ <Popover.Content {side} {align} {sideOffset}>
39
39
  {#snippet child({ wrapperProps, props, open: isOpen })}
40
- {#if isOpen}
41
- <div {...wrapperProps} class={wrapperProps.class as any}>
42
- <div
43
- {...props}
44
- class={base({ class: clsx(className, props.class as any) })}
45
- in:enterExit={{
46
- duration: 200,
47
- easing: easeEmphasizedDecel,
48
- mode: 'scale'
49
- }}
50
- out:enterExit={{
51
- duration: 150,
52
- easing: easeEmphasizedAccel,
53
- mode: 'scale'
54
- }}
55
- >
56
- {#if title || showClose}
57
- <div class={header()}>
58
- {#if title}
59
- <Title class={titleCls()}>{title}</Title>
60
- {/if}
61
- {#if showClose}
62
- <Popover.Close>
63
- {#snippet child({ props: closeProps })}
64
- <div class={close()}>
65
- <ButtonIcon
66
- {...closeProps}
67
- iconProps={{ name: 'close' }}
68
- variant="text"
69
- size="sm"
70
- />
71
- </div>
72
- {/snippet}
73
- </Popover.Close>
74
- {/if}
75
- </div>
76
- {/if}
77
- {@render children()}
78
- </div>
40
+ <div {...wrapperProps} class={wrapperProps.class as any}>
41
+ <div
42
+ {...props}
43
+ class={base({ class: clsx(className, props.class as any) })}
44
+ {@attach presence(() => isOpen, enterExit.scale)}
45
+ >
46
+ {#if title || showClose}
47
+ <div class={header()}>
48
+ {#if title}
49
+ <Title class={titleCls()}>{title}</Title>
50
+ {/if}
51
+ {#if showClose}
52
+ <Popover.Close>
53
+ {#snippet child({ props: closeProps })}
54
+ <div class={close()}>
55
+ <ButtonIcon
56
+ {...closeProps}
57
+ iconProps={{ name: 'close' }}
58
+ variant="text"
59
+ size="sm"
60
+ />
61
+ </div>
62
+ {/snippet}
63
+ </Popover.Close>
64
+ {/if}
65
+ </div>
66
+ {/if}
67
+ {@render children()}
79
68
  </div>
80
- {/if}
69
+ </div>
81
70
  {/snippet}
82
71
  </Popover.Content>
83
72
  </Popover.Portal>
@@ -3,21 +3,21 @@ export type PopoverVariants = VariantProps<typeof popover>;
3
3
  export declare const popover: import("tailwind-variants").TVReturnType<{
4
4
  [key: string]: {
5
5
  [key: string]: import("tailwind-variants").ClassValue | {
6
+ title?: import("tailwind-variants").ClassValue;
6
7
  close?: import("tailwind-variants").ClassValue;
7
8
  base?: import("tailwind-variants").ClassValue;
8
9
  body?: import("tailwind-variants").ClassValue;
9
10
  header?: import("tailwind-variants").ClassValue;
10
- title?: import("tailwind-variants").ClassValue;
11
11
  };
12
12
  };
13
13
  } | {
14
14
  [x: string]: {
15
15
  [x: string]: import("tailwind-variants").ClassValue | {
16
+ title?: import("tailwind-variants").ClassValue;
16
17
  close?: import("tailwind-variants").ClassValue;
17
18
  base?: import("tailwind-variants").ClassValue;
18
19
  body?: import("tailwind-variants").ClassValue;
19
20
  header?: import("tailwind-variants").ClassValue;
20
- title?: import("tailwind-variants").ClassValue;
21
21
  };
22
22
  };
23
23
  } | {}, {
@@ -29,11 +29,11 @@ export declare const popover: import("tailwind-variants").TVReturnType<{
29
29
  }, undefined, {
30
30
  [key: string]: {
31
31
  [key: string]: import("tailwind-variants").ClassValue | {
32
+ title?: import("tailwind-variants").ClassValue;
32
33
  close?: import("tailwind-variants").ClassValue;
33
34
  base?: import("tailwind-variants").ClassValue;
34
35
  body?: import("tailwind-variants").ClassValue;
35
36
  header?: import("tailwind-variants").ClassValue;
36
- title?: import("tailwind-variants").ClassValue;
37
37
  };
38
38
  };
39
39
  } | {}, {
@@ -3,8 +3,8 @@ export const scrollArea = tv({
3
3
  slots: {
4
4
  root: 'relative overflow-hidden',
5
5
  viewport: 'size-full rounded-[inherit]',
6
- scrollbar: 'flex touch-none select-none transition-colors duration-150 ease-out',
7
- thumb: 'relative flex-1 rounded-full bg-md-sys-color-outline-variant hover:bg-md-sys-color-secondary active:bg-md-sys-color-secondary transition-colors duration-150',
6
+ scrollbar: 'flex touch-none select-none transition-colors md-sys-motion-fast-effects',
7
+ thumb: 'relative flex-1 rounded-full bg-md-sys-color-outline-variant hover:bg-md-sys-color-secondary active:bg-md-sys-color-secondary transition-colors md-sys-motion-fast-effects',
8
8
  corner: 'bg-md-sys-color-surface-container-highest'
9
9
  },
10
10
  variants: {
@@ -1,9 +1,6 @@
1
1
  <!--
2
- SideSheet has no `open` prop of its own — mounting it calls the native <dialog>'s showModal()
3
- immediately, so visibility is controlled by conditionally rendering the component
4
- ({#if open}<SideSheet ...>{/if}), same as BottomSheet. `close` is a required callback the
5
- sheet invokes on Esc or backdrop click — the consumer is expected to flip its own `open` flag
6
- to false in response.
2
+ SideSheet is controlled with `bind:open`: it dismisses itself (close button, Esc, backdrop
3
+ click) by setting `open` to false, and stays mounted until its exit animation finishes.
7
4
  -->
8
5
  <script module lang="ts">
9
6
  import { defineMeta } from '@storybook/addon-svelte-csf';
@@ -31,16 +28,14 @@
31
28
  {#snippet template(args)}
32
29
  <div class="p-6">
33
30
  <Button onclick={() => (playgroundOpen = true)}>Show side sheet</Button>
34
- {#if playgroundOpen}
35
- <SideSheet headline={args.headline} close={() => (playgroundOpen = false)}>
36
- <div class="flex flex-col gap-3 px-6 pb-6">
37
- <Body class="text-md-sys-color-on-surface-variant">
38
- Supplementary content and actions, anchored to the edge of the screen while the main
39
- content stays visible.
40
- </Body>
41
- </div>
42
- </SideSheet>
43
- {/if}
31
+ <SideSheet headline={args.headline} bind:open={playgroundOpen}>
32
+ <div class="flex flex-col gap-3 px-6 pb-6">
33
+ <Body class="text-md-sys-color-on-surface-variant">
34
+ Supplementary content and actions, anchored to the edge of the screen while the main
35
+ content stays visible.
36
+ </Body>
37
+ </div>
38
+ </SideSheet>
44
39
  </div>
45
40
  {/snippet}
46
41
  </Story>
@@ -4,69 +4,69 @@ Side sheets are supplementary surfaces which are anchored to the edge of the scr
4
4
 
5
5
  Like BottomSheet, SideSheet owns its own overlay: it renders as a native `<dialog>` pinned to
6
6
  the right edge of the screen, dims the rest of the app behind a backdrop, and slides in/out.
7
- Mounting it calls `showModal()` immediately, so visibility is controlled by conditionally
8
- rendering the component ({#if open}<SideSheet ...>{/if}).
7
+ Visibility is controlled by `bind:open`; the sheet stays mounted until its exit animation
8
+ finishes. Conditionally rendering it ({#if open}<SideSheet close={…} />{/if}) still works, but
9
+ skips the exit animation.
9
10
 
10
11
  @see https://m3.material.io/components/side-sheets/guidelines
11
12
  -->
12
13
  <script lang="ts">
13
14
  import type { SideSheetProps } from './types.js';
14
- import type { TransitionConfig } from 'svelte/transition';
15
15
  import ButtonIcon from '../../buttons/ButtonIcon.svelte';
16
- import { easeEmphasizedAccel, easeEmphasizedDecel } from '../../../animation/easing.js';
17
- import { outroClass } from '../../../animation/outroClass.js';
16
+ import { enterExit, Presence } from '../../../animation/index.js';
18
17
 
19
- let { headline, children, close }: SideSheetProps = $props();
18
+ let { headline, children, open = $bindable(true), close }: SideSheetProps = $props();
20
19
 
21
- const open = (node: HTMLDialogElement) => node.showModal();
20
+ const sheet = new Presence(() => open);
22
21
 
23
- const slideAnim = (
24
- node: HTMLDialogElement,
25
- options: { duration: number; easing: typeof easeEmphasizedDecel }
26
- ): TransitionConfig => {
27
- const width = node.getBoundingClientRect().width;
28
- return {
29
- duration: options.duration,
30
- easing: options.easing,
31
- css: (t) => `transform: translateX(${(1 - t) * width}px)`
32
- };
22
+ const showModal = (node: HTMLDialogElement) => node.showModal();
23
+
24
+ const dismiss = () => {
25
+ open = false;
26
+ close?.();
33
27
  };
34
28
  </script>
35
29
 
36
- <dialog
37
- class="bg-md-sys-color-surface-container-low text-md-sys-color-on-surface fixed inset-y-0 right-0 left-auto m-0 h-full w-full max-w-sm rounded-l-md"
38
- use:open
39
- use:outroClass
40
- oncancel={(e) => {
41
- e.preventDefault();
42
- close();
43
- }}
44
- onmousedown={(e) => {
45
- if (e.target != e.currentTarget) return;
46
- close();
47
- }}
48
- in:slideAnim={{ easing: easeEmphasizedDecel, duration: 400 }}
49
- out:slideAnim={{ easing: easeEmphasizedAccel, duration: 300 }}
50
- >
51
- <div class="flex h-full flex-col">
52
- <div class="flex items-center justify-between p-6">
53
- <span class="md-sys-typescale-title-large text-md-sys-color-on-surface-variant"
54
- >{headline}</span
55
- >
56
- <ButtonIcon type="button" variant="text" iconProps={{ name: 'close' }} onclick={close} />
30
+ {#if sheet.mounted}
31
+ <dialog
32
+ class="bg-md-sys-color-surface-container-low text-md-sys-color-on-surface fixed inset-y-0 right-0 left-auto m-0 h-full w-full max-w-sm rounded-l-md"
33
+ data-state={open ? 'open' : 'closed'}
34
+ {@attach showModal}
35
+ {@attach sheet.attach(enterExit.sideSheet)}
36
+ oncancel={(e) => {
37
+ e.preventDefault();
38
+ dismiss();
39
+ }}
40
+ onmousedown={(e) => {
41
+ if (e.target != e.currentTarget) return;
42
+ dismiss();
43
+ }}
44
+ >
45
+ <div class="flex h-full flex-col">
46
+ <div class="flex items-center justify-between p-6">
47
+ <span class="md-sys-typescale-title-large text-md-sys-color-on-surface-variant"
48
+ >{headline}</span
49
+ >
50
+ <ButtonIcon type="button" variant="text" iconProps={{ name: 'close' }} onclick={dismiss} />
51
+ </div>
52
+ {@render children()}
57
53
  </div>
58
- {@render children()}
59
- </div>
60
- </dialog>
54
+ </dialog>
55
+ {/if}
61
56
 
62
57
  <style>
63
58
  dialog::backdrop {
64
59
  background-color: rgba(0, 0, 0, 0.5);
65
- animation: backdrop 400ms;
60
+ animation: backdrop var(--md-sys-motion-duration-effects-spring)
61
+ var(--md-sys-motion-timing-function-effects-spring);
62
+ }
63
+ dialog[data-state='closed'] {
64
+ pointer-events: none;
66
65
  }
67
- dialog:global(.leaving)::backdrop {
66
+ dialog[data-state='closed']::backdrop {
68
67
  background-color: transparent;
69
- animation: backdropReverse 400ms;
68
+ animation: backdropReverse var(--md-sys-motion-duration-fast-effects-spring)
69
+ var(--md-sys-motion-timing-function-fast-effects-spring);
70
70
  }
71
71
  @keyframes backdrop {
72
72
  from {
@@ -4,11 +4,12 @@ import type { SideSheetProps } from './types.js';
4
4
  *
5
5
  * Like BottomSheet, SideSheet owns its own overlay: it renders as a native `<dialog>` pinned to
6
6
  * the right edge of the screen, dims the rest of the app behind a backdrop, and slides in/out.
7
- * Mounting it calls `showModal()` immediately, so visibility is controlled by conditionally
8
- * rendering the component ({#if open}<SideSheet ...>{/if}).
7
+ * Visibility is controlled by `bind:open`; the sheet stays mounted until its exit animation
8
+ * finishes. Conditionally rendering it ({#if open}<SideSheet close={…} />{/if}) still works, but
9
+ * skips the exit animation.
9
10
  *
10
11
  * @see https://m3.material.io/components/side-sheets/guidelines
11
12
  */
12
- declare const SideSheet: import("svelte").Component<SideSheetProps, {}, "">;
13
+ declare const SideSheet: import("svelte").Component<SideSheetProps, {}, "open">;
13
14
  type SideSheet = ReturnType<typeof SideSheet>;
14
15
  export default SideSheet;
@@ -12,7 +12,13 @@ export interface SideSheetProps {
12
12
  */
13
13
  children: Snippet;
14
14
  /**
15
- * Callback function to close the side sheet.
15
+ * Whether the sheet is shown. Bind it (`bind:open`) so the sheet can play its exit animation
16
+ * before unmounting.
17
+ * @default true
16
18
  */
17
- close: () => void;
19
+ open?: boolean;
20
+ /**
21
+ * Called when the user dismisses the sheet (close button, Esc, backdrop click).
22
+ */
23
+ close?: () => void;
18
24
  }
@@ -1,40 +1,44 @@
1
1
  <!--
2
2
  @component
3
- DateField is a text field that allows users to enter a date or pick it from a calendar.
3
+ DateField is a text field that allows users to enter a date or pick it from a docked calendar.
4
4
 
5
5
  @see https://m3.material.io/components/date-pickers/guidelines
6
6
  -->
7
7
  <script lang="ts">
8
- import { DatePicker } from 'bits-ui';
8
+ import { DatePicker, useId } from 'bits-ui';
9
+ import { enterExit, presence } from '../../animation/index.js';
9
10
  import ButtonIcon from '../buttons/ButtonIcon.svelte';
10
11
  import Icon from '../../utils/icon/Icon.svelte';
11
12
  import Layer from '../../utils/Layer.svelte';
12
13
  import type { DateFieldProps } from './types';
13
- import { dateField } from './theme';
14
+ import { dateCalendar, dateField, dateSegment } from './theme';
14
15
 
15
16
  let {
16
17
  value = $bindable(),
17
18
  label,
18
- id,
19
+ id = useId(),
19
20
  required = false,
20
21
  disabled = false,
21
22
  error = false,
22
23
  variant = 'outlined',
23
24
  supportingText,
24
25
  name,
26
+ locale,
27
+ weekStartsOn,
25
28
  leadingIconProps
26
- }: DateFieldProps & { leadingIconProps?: any } = $props();
29
+ }: DateFieldProps = $props();
27
30
 
28
31
  const cls = $derived(dateField({ disabled, error, variant }));
32
+ const calendar = dateCalendar();
29
33
  </script>
30
34
 
31
35
  <DatePicker.Root
32
36
  bind:value
33
- weekStartsOn={1}
37
+ {weekStartsOn}
34
38
  {required}
35
39
  {disabled}
36
- locale="ru"
37
- weekdayFormat="short"
40
+ {locale}
41
+ weekdayFormat="narrow"
38
42
  fixedWeeks
39
43
  >
40
44
  <div class="relative w-full">
@@ -47,45 +51,34 @@ DateField is a text field that allows users to enter a date or pick it from a ca
47
51
  <DatePicker.Input class={cls.input()} {name} id={id ?? undefined}>
48
52
  {#snippet children({ segments })}
49
53
  <div class={cls.segments()}>
50
- <div class="flex items-center justify-between">
51
- <div class="flex">
52
- {#each segments as { part, value }}
53
- {#if part === 'literal'}
54
- <DatePicker.Segment
55
- {part}
56
- class="text-md-sys-color-on-surface-variant px-0.5 select-none first:pl-0"
57
- >
58
- {value}
59
- </DatePicker.Segment>
60
- {:else}
61
- <DatePicker.Segment
62
- {part}
63
- class="text-md-sys-color-on-surface hover:bg-md-sys-color-on-surface/8 focus:bg-md-sys-color-primary-container focus:text-md-sys-color-on-primary-container focus:outline-md-sys-color-primary
64
- aria-[valuetext=Empty]:text-md-sys-color-on-surface-variant cursor-default
65
- rounded-xs
66
- px-1
67
- py-0.5
68
- transition-colors
69
- duration-100 select-none focus:outline-2
70
- focus-visible:ring-0! focus-visible:ring-offset-0!"
71
- >
72
- {value}
73
- </DatePicker.Segment>
74
- {/if}
75
- {/each}
76
- </div>
54
+ <div class="flex">
55
+ {#each segments as { part, value: segment }}
56
+ <DatePicker.Segment
57
+ {part}
58
+ class={dateSegment({ part: part === 'literal' ? 'literal' : 'value' })}
59
+ >
60
+ {segment}
61
+ </DatePicker.Segment>
62
+ {/each}
77
63
  </div>
78
64
  </div>
79
65
  {/snippet}
80
66
  </DatePicker.Input>
81
67
 
82
- <label class={cls.label()} for={id}>
68
+ <DatePicker.Label class={cls.label()}>
83
69
  {label}{#if required}<span class={cls.requiredAsterisk()} aria-hidden="true">*</span>{/if}
84
- </label>
70
+ </DatePicker.Label>
85
71
  </div>
86
72
 
87
- <DatePicker.Trigger class={cls.trailingIcon({ class: 'flex items-center justify-center' })}>
88
- <Icon name="calendar_month" />
73
+ <DatePicker.Trigger>
74
+ {#snippet child({ props })}
75
+ <ButtonIcon
76
+ {...props}
77
+ {disabled}
78
+ class={cls.trailingIcon()}
79
+ iconProps={{ name: 'calendar_month' }}
80
+ />
81
+ {/snippet}
89
82
  </DatePicker.Trigger>
90
83
 
91
84
  {#if variant === 'outlined'}
@@ -103,62 +96,62 @@ DateField is a text field that allows users to enter a date or pick it from a ca
103
96
  </div>
104
97
  {/if}
105
98
  </div>
106
- <DatePicker.Portal>
107
- <DatePicker.Content sideOffset={6} class="z-[100]">
108
- <DatePicker.Calendar
109
- class="bg-md-sys-color-surface-container-high shadow-elevation-3 h-104 w-90 rounded-lg p-6"
110
- >
111
- {#snippet children({ months, weekdays })}
112
- <DatePicker.Header
113
- class="text-md-sys-color-on-surface-variant z-10 flex w-full items-center justify-between pb-7.5"
114
- >
115
- <DatePicker.PrevButton>
116
- <ButtonIcon type="button" iconProps={{ name: 'chevron_left' }} />
117
- </DatePicker.PrevButton>
118
- <DatePicker.Heading class="md-sys-typescale-label-large" />
119
- <DatePicker.NextButton>
120
- <ButtonIcon type="button" iconProps={{ name: 'chevron_right' }} />
121
- </DatePicker.NextButton>
122
- </DatePicker.Header>
123
99
 
124
- {#each months as month}
125
- <DatePicker.Grid class="w-full border-collapse select-none">
126
- <DatePicker.GridHead>
127
- <DatePicker.GridRow class="flex w-full justify-between">
128
- {#each weekdays as day}
129
- <DatePicker.HeadCell
130
- class="md-sys-typescale-label-large text-md-sys-color-on-surface-variant size-10"
131
- >
132
- {day}
133
- </DatePicker.HeadCell>
134
- {/each}
135
- </DatePicker.GridRow>
136
- </DatePicker.GridHead>
137
- <DatePicker.GridBody>
138
- {#each month.weeks as weekDates}
139
- <DatePicker.GridRow class="flex w-full justify-between">
140
- {#each weekDates as date}
141
- <DatePicker.Cell
142
- {date}
143
- month={month.value}
144
- class="md-sys-typescale-body-large text-center"
145
- >
146
- <DatePicker.Day
147
- class="group data-disabled:text-md-sys-color-on-surface/38 data-selected:bg-md-sys-color-primary data-selected:text-md-sys-color-on-primary data-unavailable:text-md-sys-color-on-surface/38 relative flex size-10 items-center justify-center rounded-full bg-transparent p-0 data-disabled:cursor-not-allowed data-outside-month:pointer-events-none data-unavailable:line-through"
148
- >
149
- <Layer />
100
+ <DatePicker.Portal>
101
+ <DatePicker.Content sideOffset={4} align="start">
102
+ {#snippet child({ wrapperProps, props, open })}
103
+ <div {...wrapperProps}>
104
+ <div {...props} {@attach presence(() => open, enterExit.scale)}>
105
+ <DatePicker.Calendar class={calendar.surface()}>
106
+ {#snippet children({ months, weekdays })}
107
+ <DatePicker.Header class={calendar.header()}>
108
+ <DatePicker.Heading class={calendar.heading()} />
109
+ <div class={calendar.nav()}>
110
+ <DatePicker.PrevButton>
111
+ {#snippet child({ props })}
112
+ <ButtonIcon {...props} iconProps={{ name: 'chevron_left' }} />
113
+ {/snippet}
114
+ </DatePicker.PrevButton>
115
+ <DatePicker.NextButton>
116
+ {#snippet child({ props })}
117
+ <ButtonIcon {...props} iconProps={{ name: 'chevron_right' }} />
118
+ {/snippet}
119
+ </DatePicker.NextButton>
120
+ </div>
121
+ </DatePicker.Header>
150
122
 
151
- {date.day}
152
- </DatePicker.Day>
153
- </DatePicker.Cell>
154
- {/each}
155
- </DatePicker.GridRow>
123
+ {#each months as month (month.value.toString())}
124
+ <DatePicker.Grid class={calendar.grid()}>
125
+ <DatePicker.GridHead>
126
+ <DatePicker.GridRow class={calendar.row()}>
127
+ {#each weekdays as day, i (i)}
128
+ <DatePicker.HeadCell class={calendar.weekday()}>
129
+ {day}
130
+ </DatePicker.HeadCell>
131
+ {/each}
132
+ </DatePicker.GridRow>
133
+ </DatePicker.GridHead>
134
+ <DatePicker.GridBody>
135
+ {#each month.weeks as weekDates, i (i)}
136
+ <DatePicker.GridRow class={calendar.row()}>
137
+ {#each weekDates as date (date.toString())}
138
+ <DatePicker.Cell {date} month={month.value} class={calendar.cell()}>
139
+ <DatePicker.Day class={calendar.day()}>
140
+ <Layer />
141
+ {date.day}
142
+ </DatePicker.Day>
143
+ </DatePicker.Cell>
144
+ {/each}
145
+ </DatePicker.GridRow>
146
+ {/each}
147
+ </DatePicker.GridBody>
148
+ </DatePicker.Grid>
156
149
  {/each}
157
- </DatePicker.GridBody>
158
- </DatePicker.Grid>
159
- {/each}
160
- {/snippet}
161
- </DatePicker.Calendar>
150
+ {/snippet}
151
+ </DatePicker.Calendar>
152
+ </div>
153
+ </div>
154
+ {/snippet}
162
155
  </DatePicker.Content>
163
156
  </DatePicker.Portal>
164
157
  </DatePicker.Root>
@@ -1,12 +1,9 @@
1
1
  import type { DateFieldProps } from './types';
2
- type $$ComponentProps = DateFieldProps & {
3
- leadingIconProps?: any;
4
- };
5
2
  /**
6
- * DateField is a text field that allows users to enter a date or pick it from a calendar.
3
+ * DateField is a text field that allows users to enter a date or pick it from a docked calendar.
7
4
  *
8
5
  * @see https://m3.material.io/components/date-pickers/guidelines
9
6
  */
10
- declare const DateField: import("svelte").Component<$$ComponentProps, {}, "value">;
7
+ declare const DateField: import("svelte").Component<DateFieldProps, {}, "value">;
11
8
  type DateField = ReturnType<typeof DateField>;
12
9
  export default DateField;