@dryui/ui 1.5.0 → 1.6.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 (86) hide show
  1. package/dist/app-frame/app-frame.svelte +10 -3
  2. package/dist/button-group/context.svelte.js +4 -7
  3. package/dist/calendar/calendar-root.svelte +15 -32
  4. package/dist/chip-group/context.svelte.d.ts +2 -4
  5. package/dist/chip-group/context.svelte.js +2 -9
  6. package/dist/context-menu/context-menu-content.svelte +24 -12
  7. package/dist/context-menu/context-menu-group.svelte +3 -2
  8. package/dist/context-menu/context-menu-item.svelte +8 -61
  9. package/dist/context-menu/context-menu-label.svelte +3 -11
  10. package/dist/context-menu/context-menu-root.svelte +10 -29
  11. package/dist/context-menu/context-menu-separator.svelte +2 -9
  12. package/dist/context-menu/context.svelte.d.ts +2 -12
  13. package/dist/date-picker/datepicker-content.svelte +11 -81
  14. package/dist/date-picker/datepicker-content.svelte.d.ts +1 -1
  15. package/dist/date-picker/datepicker-input-root.svelte +39 -47
  16. package/dist/date-range-picker/date-range-picker-content.svelte +11 -75
  17. package/dist/date-range-picker/date-range-picker-content.svelte.d.ts +1 -1
  18. package/dist/date-range-picker/date-range-picker-root.svelte +44 -49
  19. package/dist/drag-and-drop/group-context.svelte.d.ts +1 -1
  20. package/dist/drag-and-drop/group-context.svelte.js +4 -4
  21. package/dist/dropdown-menu/context.svelte.d.ts +2 -8
  22. package/dist/dropdown-menu/dropdown-menu-content.svelte +15 -3
  23. package/dist/dropdown-menu/dropdown-menu-group.svelte +3 -2
  24. package/dist/dropdown-menu/dropdown-menu-item.svelte +8 -61
  25. package/dist/dropdown-menu/dropdown-menu-label.svelte +3 -11
  26. package/dist/dropdown-menu/dropdown-menu-root.svelte +10 -21
  27. package/dist/dropdown-menu/dropdown-menu-separator.svelte +2 -9
  28. package/dist/flip-card/context.svelte.d.ts +5 -0
  29. package/dist/flip-card/context.svelte.js +2 -0
  30. package/dist/flip-card/flip-card-back.svelte +2 -2
  31. package/dist/flip-card/flip-card-root.svelte +42 -15
  32. package/dist/heading/heading.svelte +10 -1
  33. package/dist/heading/heading.svelte.d.ts +1 -0
  34. package/dist/heading/index.d.ts +1 -0
  35. package/dist/hover-card/hover-card-content.svelte +9 -21
  36. package/dist/hover-card/hover-card-root.svelte +2 -2
  37. package/dist/hover-card/hover-card-root.svelte.d.ts +4 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +1 -0
  40. package/dist/internal/anchored-overlay-content.svelte.d.ts +20 -0
  41. package/dist/internal/anchored-overlay-content.svelte.js +28 -0
  42. package/dist/internal/date-family-controller.svelte.d.ts +45 -0
  43. package/dist/internal/date-family-controller.svelte.js +99 -0
  44. package/dist/internal/menu-group.svelte +15 -0
  45. package/dist/internal/menu-group.svelte.d.ts +9 -0
  46. package/dist/internal/menu-item.svelte +82 -0
  47. package/dist/internal/menu-item.svelte.d.ts +11 -0
  48. package/dist/internal/menu-label.svelte +24 -0
  49. package/dist/internal/menu-label.svelte.d.ts +9 -0
  50. package/dist/internal/menu-root-state.svelte.d.ts +24 -0
  51. package/dist/internal/menu-root-state.svelte.js +42 -0
  52. package/dist/internal/menu-separator.svelte +19 -0
  53. package/dist/internal/menu-separator.svelte.d.ts +7 -0
  54. package/dist/internal/motion.js +12 -1
  55. package/dist/internal/picker-popover-content.svelte +112 -0
  56. package/dist/internal/picker-popover-content.svelte.d.ts +16 -0
  57. package/dist/link-preview/link-preview-content.svelte +7 -10
  58. package/dist/list/list-item-icon.svelte +3 -3
  59. package/dist/list/list-item-icon.svelte.d.ts +1 -1
  60. package/dist/list/list-item-text.svelte +3 -3
  61. package/dist/list/list-item-text.svelte.d.ts +1 -1
  62. package/dist/list/list-item.svelte +58 -35
  63. package/dist/list/list-item.svelte.d.ts +8 -2
  64. package/dist/popover/popover-content.svelte +9 -11
  65. package/dist/range-calendar/range-calendar-root.svelte +13 -19
  66. package/dist/text/index.d.ts +1 -0
  67. package/dist/text/text.svelte +3 -1
  68. package/dist/text/text.svelte.d.ts +1 -0
  69. package/dist/theme-toggle/index.d.ts +18 -0
  70. package/dist/theme-toggle/index.js +3 -0
  71. package/dist/theme-toggle/theme-controller.svelte.d.ts +54 -0
  72. package/dist/theme-toggle/theme-controller.svelte.js +121 -0
  73. package/dist/theme-toggle/theme-flash.d.ts +16 -0
  74. package/dist/theme-toggle/theme-flash.js +38 -0
  75. package/dist/theme-toggle/theme-toggle.svelte +189 -0
  76. package/dist/theme-toggle/theme-toggle.svelte.d.ts +40 -0
  77. package/dist/tooltip/tooltip-content.svelte +8 -10
  78. package/dist/typography/heading.svelte +13 -89
  79. package/dist/typography/heading.svelte.d.ts +3 -8
  80. package/dist/typography/index.d.ts +8 -7
  81. package/dist/typography/text.svelte +12 -84
  82. package/dist/typography/text.svelte.d.ts +3 -10
  83. package/package.json +7 -2
  84. package/skills/dryui/SKILL.md +18 -5
  85. package/skills/dryui/rules/composition.md +1 -1
  86. package/skills/dryui/rules/theming.md +1 -2
@@ -0,0 +1,42 @@
1
+ import { generateFormId } from '@dryui/primitives';
2
+ function createBaseMenuRootState(options) {
3
+ const triggerId = generateFormId(`${options.idBase}-trigger`);
4
+ const contentId = generateFormId(`${options.idBase}-content`);
5
+ return {
6
+ triggerId,
7
+ contentId,
8
+ triggerEl: null,
9
+ show() {
10
+ options.setOpen(true);
11
+ },
12
+ close() {
13
+ options.setOpen(false);
14
+ },
15
+ toggle() {
16
+ options.setOpen(!options.getOpen());
17
+ }
18
+ };
19
+ }
20
+ export function createMenuRootState(options) {
21
+ return {
22
+ get open() {
23
+ return options.getOpen();
24
+ },
25
+ ...createBaseMenuRootState(options)
26
+ };
27
+ }
28
+ export function createPositionedMenuRootState(options) {
29
+ let position = $state({ x: 0, y: 0 });
30
+ return {
31
+ get open() {
32
+ return options.getOpen();
33
+ },
34
+ ...createBaseMenuRootState(options),
35
+ get position() {
36
+ return position;
37
+ },
38
+ set position(value) {
39
+ position = value;
40
+ }
41
+ };
42
+ }
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ className?: HTMLAttributes<HTMLDivElement>['class'];
6
+ }
7
+
8
+ let { className, ...rest }: Props = $props();
9
+ </script>
10
+
11
+ <div role="separator" class={className} {...rest}></div>
12
+
13
+ <style>
14
+ div {
15
+ height: 1px;
16
+ background: var(--dry-color-stroke-weak);
17
+ margin: var(--dry-space-1) 0;
18
+ }
19
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ className?: HTMLAttributes<HTMLDivElement>['class'];
4
+ }
5
+ declare const MenuSeparator: import("svelte").Component<Props, {}, "">;
6
+ type MenuSeparator = ReturnType<typeof MenuSeparator>;
7
+ export default MenuSeparator;
@@ -11,8 +11,19 @@ export function supportsScrollTimelines() {
11
11
  return (CSS.supports('animation-timeline: view()') || CSS.supports('scroll-timeline-name: --dry-scroll'));
12
12
  }
13
13
  export function extractThemeColor(property, element) {
14
+ if (typeof document === 'undefined') {
15
+ return [0, 0, 0];
16
+ }
17
+ const readComputedStyle = typeof getComputedStyle === 'function'
18
+ ? getComputedStyle
19
+ : typeof window !== 'undefined' && typeof window.getComputedStyle === 'function'
20
+ ? window.getComputedStyle.bind(window)
21
+ : null;
22
+ if (!readComputedStyle) {
23
+ return [0, 0, 0];
24
+ }
14
25
  const el = element ?? document.documentElement;
15
- const value = getComputedStyle(el).getPropertyValue(property).trim();
26
+ const value = readComputedStyle(el).getPropertyValue(property).trim();
16
27
  // Parse hex (#rgb, #rrggbb)
17
28
  const hexMatch = value.match(/^#([0-9a-f]{3,8})$/i);
18
29
  if (hexMatch) {
@@ -0,0 +1,112 @@
1
+ <script lang="ts">
2
+ import { fromAction } from 'svelte/attachments';
3
+ import type { Snippet } from 'svelte';
4
+ import type { HTMLAttributes } from 'svelte/elements';
5
+ import { createAnchoredPopover, type Placement } from '@dryui/primitives';
6
+ import type { PickerPopoverController } from './date-family-controller.svelte.js';
7
+
8
+ interface Props extends HTMLAttributes<HTMLDivElement> {
9
+ controller: PickerPopoverController;
10
+ dataAttribute: 'data-dp-content' | 'data-drp-content';
11
+ placement?: Placement;
12
+ offset?: number;
13
+ contentClass?: HTMLAttributes<HTMLDivElement>['class'];
14
+ contentStyle?: HTMLAttributes<HTMLDivElement>['style'];
15
+ children: Snippet;
16
+ }
17
+
18
+ let {
19
+ controller,
20
+ dataAttribute,
21
+ placement = 'bottom-start',
22
+ offset = 8,
23
+ contentClass,
24
+ contentStyle,
25
+ children,
26
+ ...rest
27
+ }: Props = $props();
28
+
29
+ let el = $state<HTMLDivElement | null>(null);
30
+
31
+ const markerAttrs = $derived.by<Record<string, string>>(() => ({
32
+ 'data-picker-popover-content': '',
33
+ [dataAttribute]: ''
34
+ }));
35
+
36
+ function attachContent(node: HTMLDivElement) {
37
+ el = node;
38
+
39
+ return () => {
40
+ if (el === node) {
41
+ el = null;
42
+ }
43
+ };
44
+ }
45
+
46
+ const popover = createAnchoredPopover({
47
+ triggerEl: () => controller.triggerEl,
48
+ contentEl: () => el ?? null,
49
+ open: () => controller.open,
50
+ placement: () => placement,
51
+ offset: () => offset
52
+ });
53
+ </script>
54
+
55
+ <div
56
+ {@attach attachContent}
57
+ {@attach fromAction(popover.applyPosition, () => contentStyle)}
58
+ popover="auto"
59
+ role="dialog"
60
+ id={controller.contentId}
61
+ aria-labelledby={controller.triggerId}
62
+ data-state={controller.open ? 'open' : 'closed'}
63
+ class={contentClass}
64
+ {...markerAttrs}
65
+ ontoggle={(e) => {
66
+ const newState = (e as ToggleEvent).newState === 'open';
67
+
68
+ if (newState && !controller.open) {
69
+ controller.show();
70
+ } else if (!newState && controller.open) {
71
+ controller.close();
72
+ }
73
+ }}
74
+ {...rest}
75
+ >
76
+ {@render children()}
77
+ </div>
78
+
79
+ <style>
80
+ [data-picker-popover-content] {
81
+ inset: unset;
82
+ margin: 0;
83
+ display: inline-grid;
84
+ padding: var(--dry-space-3);
85
+ border: 1px solid var(--dry-color-stroke-weak);
86
+ border-radius: var(--dry-radius-lg);
87
+ background: var(--dry-color-bg-overlay);
88
+ box-shadow: var(--dry-shadow-lg);
89
+ color: var(--dry-color-text-strong);
90
+ transition:
91
+ opacity var(--dry-duration-fast) var(--dry-ease-emphasized),
92
+ transform var(--dry-duration-fast) var(--dry-ease-emphasized);
93
+ }
94
+
95
+ [data-picker-popover-content]:not(:popover-open) {
96
+ display: none;
97
+ }
98
+
99
+ [data-picker-popover-content]:popover-open {
100
+ display: inline-grid;
101
+ opacity: 1;
102
+ transform: translateY(0) scale(1);
103
+ }
104
+
105
+ @starting-style {
106
+ [data-picker-popover-content]:popover-open {
107
+ opacity: 0;
108
+ transform: translateY(calc(var(--dry-motion-distance-xs) * -1))
109
+ scale(var(--dry-motion-scale-enter));
110
+ }
111
+ }
112
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type Placement } from '@dryui/primitives';
4
+ import type { PickerPopoverController } from './date-family-controller.svelte.js';
5
+ interface Props extends HTMLAttributes<HTMLDivElement> {
6
+ controller: PickerPopoverController;
7
+ dataAttribute: 'data-dp-content' | 'data-drp-content';
8
+ placement?: Placement;
9
+ offset?: number;
10
+ contentClass?: HTMLAttributes<HTMLDivElement>['class'];
11
+ contentStyle?: HTMLAttributes<HTMLDivElement>['style'];
12
+ children: Snippet;
13
+ }
14
+ declare const PickerPopoverContent: import("svelte").Component<Props, {}, "">;
15
+ type PickerPopoverContent = ReturnType<typeof PickerPopoverContent>;
16
+ export default PickerPopoverContent;
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { createAnchoredPopover } from '@dryui/primitives';
5
4
  import type { Placement } from '@dryui/primitives';
5
+ import { createAnchoredOverlayContent } from '../internal/anchored-overlay-content.svelte.js';
6
6
  import { getLinkPreviewCtx } from './context.svelte.js';
7
7
 
8
8
  interface Props extends HTMLAttributes<HTMLDivElement> {
@@ -22,25 +22,22 @@
22
22
 
23
23
  const ctx = getLinkPreviewCtx();
24
24
 
25
- let contentEl = $state<HTMLDivElement>();
26
-
27
- const popover = createAnchoredPopover({
28
- triggerEl: () => ctx.triggerEl,
29
- contentEl: () => contentEl ?? null,
30
- open: () => ctx.open,
25
+ const overlay = createAnchoredOverlayContent({
26
+ ctx,
31
27
  placement: () => placement,
32
- offset: () => offset
28
+ offset: () => offset,
29
+ style: () => style
33
30
  });
34
31
  </script>
35
32
 
36
33
  <div
37
- bind:this={contentEl}
34
+ {@attach overlay.bindContent}
35
+ {@attach overlay.position}
38
36
  id={ctx.contentId}
39
37
  role="tooltip"
40
38
  popover="manual"
41
39
  data-link-preview-content
42
40
  data-state={ctx.open ? 'open' : 'closed'}
43
- use:popover.applyPosition={style}
44
41
  onmouseenter={() => ctx.showImmediate()}
45
42
  onmouseleave={() => ctx.close()}
46
43
  class={className}
@@ -2,16 +2,16 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
 
5
- interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ interface Props extends HTMLAttributes<HTMLSpanElement> {
6
6
  children: Snippet;
7
7
  }
8
8
 
9
9
  let { class: className, children, ...rest }: Props = $props();
10
10
  </script>
11
11
 
12
- <div data-list-item-icon class={className} {...rest}>
12
+ <span data-list-item-icon class={className} {...rest}>
13
13
  {@render children()}
14
- </div>
14
+ </span>
15
15
 
16
16
  <style>
17
17
  [data-list-item-icon] {
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ interface Props extends HTMLAttributes<HTMLSpanElement> {
4
4
  children: Snippet;
5
5
  }
6
6
  declare const ListItemIcon: import("svelte").Component<Props, {}, "">;
@@ -2,7 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
 
5
- interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ interface Props extends HTMLAttributes<HTMLSpanElement> {
6
6
  primary?: Snippet;
7
7
  secondary?: Snippet;
8
8
  children?: Snippet;
@@ -11,7 +11,7 @@
11
11
  let { class: className, primary, secondary, children, ...rest }: Props = $props();
12
12
  </script>
13
13
 
14
- <div data-list-item-text class={className} {...rest}>
14
+ <span data-list-item-text class={className} {...rest}>
15
15
  {#if primary}
16
16
  <span data-list-item-primary>{@render primary()}</span>
17
17
  {:else if children}
@@ -20,7 +20,7 @@
20
20
  {#if secondary}
21
21
  <span data-list-item-secondary>{@render secondary()}</span>
22
22
  {/if}
23
- </div>
23
+ </span>
24
24
 
25
25
  <style>
26
26
  [data-list-item-text] {
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ interface Props extends HTMLAttributes<HTMLSpanElement> {
4
4
  primary?: Snippet;
5
5
  secondary?: Snippet;
6
6
  children?: Snippet;
@@ -1,12 +1,22 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { ClassValue, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
4
+ import Button from '../button/button.svelte';
4
5
  import { getListCtx } from './context.svelte.js';
5
6
 
6
- interface Props extends HTMLAttributes<HTMLLIElement> {
7
+ interface Props extends Omit<
8
+ HTMLAttributes<HTMLLIElement>,
9
+ 'children' | 'class' | 'onclick' | 'onkeydown' | 'aria-label' | 'aria-labelledby' | 'title'
10
+ > {
7
11
  interactive?: boolean;
8
12
  disabled?: boolean;
13
+ class?: ClassValue;
9
14
  children: Snippet;
15
+ onclick?: HTMLButtonAttributes['onclick'];
16
+ onkeydown?: HTMLButtonAttributes['onkeydown'];
17
+ 'aria-label'?: HTMLButtonAttributes['aria-label'];
18
+ 'aria-labelledby'?: HTMLButtonAttributes['aria-labelledby'];
19
+ title?: string;
10
20
  }
11
21
 
12
22
  let {
@@ -15,74 +25,87 @@
15
25
  class: className,
16
26
  children,
17
27
  onclick,
28
+ onkeydown,
29
+ 'aria-label': ariaLabel,
30
+ 'aria-labelledby': ariaLabelledBy,
31
+ title,
18
32
  ...rest
19
33
  }: Props = $props();
20
34
 
21
35
  const ctx = getListCtx();
22
-
23
- function handleKeydown(e: KeyboardEvent) {
24
- if (!interactive || disabled) return;
25
- if (e.key === 'Enter' || e.key === ' ') {
26
- e.preventDefault();
27
- if (onclick) {
28
- (onclick as (e: Event) => void)(e);
29
- }
30
- }
31
- }
32
36
  </script>
33
37
 
34
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
35
38
  <li
36
39
  data-list-item
37
40
  data-interactive={interactive || undefined}
38
41
  data-disabled={disabled || undefined}
39
42
  data-dense={ctx.dense || undefined}
40
- role={interactive ? 'button' : undefined}
41
- tabindex={interactive && !disabled ? 0 : undefined}
42
- aria-disabled={disabled || undefined}
43
43
  class={className}
44
- {onclick}
45
- onkeydown={interactive ? handleKeydown : undefined}
46
44
  {...rest}
47
45
  >
48
- {@render children()}
46
+ {#if interactive}
47
+ <Button
48
+ variant="secondary"
49
+ {disabled}
50
+ aria-label={ariaLabel}
51
+ aria-labelledby={ariaLabelledBy}
52
+ {title}
53
+ {onclick}
54
+ {onkeydown}
55
+ >
56
+ <span data-list-item-surface>
57
+ {@render children()}
58
+ </span>
59
+ </Button>
60
+ {:else}
61
+ <div data-list-item-surface>
62
+ {@render children()}
63
+ </div>
64
+ {/if}
49
65
  </li>
50
66
 
51
67
  <style>
52
68
  [data-list-item] {
69
+ display: grid;
70
+ list-style: none;
71
+ }
72
+
73
+ [data-list-item-surface] {
53
74
  display: grid;
54
75
  grid-template-columns: auto minmax(0, 1fr);
55
76
  align-items: start;
56
77
  gap: var(--dry-list-item-gap);
57
78
  padding: var(--dry-list-item-padding);
79
+ border: 0;
58
80
  border-radius: var(--dry-list-item-radius);
81
+ background: transparent;
82
+ color: inherit;
83
+ font: inherit;
84
+ text-align: left;
59
85
  transition:
60
86
  background var(--dry-duration-fast) var(--dry-ease-default),
61
87
  color var(--dry-duration-fast) var(--dry-ease-default);
62
88
  }
63
89
 
64
90
  [data-list-item][data-interactive='true'] {
91
+ --dry-btn-bg: transparent;
92
+ --dry-btn-border: transparent;
93
+ --dry-btn-color: inherit;
94
+ --dry-btn-padding-x: 0;
95
+ --dry-btn-padding-y: 0;
96
+ --dry-btn-min-height: 0;
97
+ --dry-btn-accent: var(--dry-list-item-active-bg);
98
+ --dry-btn-accent-fg: inherit;
99
+ --dry-btn-accent-stroke: transparent;
100
+ --dry-btn-accent-weak: var(--dry-list-item-hover-bg);
101
+ --dry-btn-on-accent: inherit;
102
+ --dry-btn-radius: var(--dry-list-item-radius);
103
+ box-shadow: none;
65
104
  cursor: pointer;
66
105
  }
67
106
 
68
- [data-list-item][data-interactive='true']:hover,
69
- [data-list-item][data-interactive='true']:focus-visible {
70
- background: var(--dry-list-item-hover-bg);
71
- }
72
-
73
- [data-list-item][data-interactive='true']:active {
74
- background: var(--dry-list-item-active-bg);
75
- }
76
-
77
- [data-list-item][data-interactive='true']:focus-visible {
78
- outline: var(--dry-focus-ring);
79
- outline-offset: -2px;
80
- }
81
-
82
107
  [data-list-item][data-disabled='true'] {
83
108
  opacity: var(--dry-state-disabled-opacity);
84
- cursor: not-allowed;
85
- pointer-events: none;
86
109
  }
87
110
 
88
111
  [data-list-item][data-dense='true'] {
@@ -1,9 +1,15 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- interface Props extends HTMLAttributes<HTMLLIElement> {
2
+ import type { ClassValue, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
3
+ interface Props extends Omit<HTMLAttributes<HTMLLIElement>, 'children' | 'class' | 'onclick' | 'onkeydown' | 'aria-label' | 'aria-labelledby' | 'title'> {
4
4
  interactive?: boolean;
5
5
  disabled?: boolean;
6
+ class?: ClassValue;
6
7
  children: Snippet;
8
+ onclick?: HTMLButtonAttributes['onclick'];
9
+ onkeydown?: HTMLButtonAttributes['onkeydown'];
10
+ 'aria-label'?: HTMLButtonAttributes['aria-label'];
11
+ 'aria-labelledby'?: HTMLButtonAttributes['aria-labelledby'];
12
+ title?: string;
7
13
  }
8
14
  declare const ListItem: import("svelte").Component<Props, {}, "">;
9
15
  type ListItem = ReturnType<typeof ListItem>;
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { createAnchoredPopover, createDismiss } from '@dryui/primitives';
4
+ import { createDismiss } from '@dryui/primitives';
5
5
  import type { Placement } from '@dryui/primitives';
6
+ import { createAnchoredOverlayContent } from '../internal/anchored-overlay-content.svelte.js';
6
7
  import { getPopoverCtx } from './context.svelte.js';
7
8
 
8
9
  interface Props extends HTMLAttributes<HTMLDivElement> {
@@ -22,20 +23,17 @@
22
23
 
23
24
  const ctx = getPopoverCtx();
24
25
 
25
- let contentEl = $state<HTMLDivElement>();
26
-
27
- const popover = createAnchoredPopover({
28
- triggerEl: () => ctx.triggerEl,
29
- contentEl: () => contentEl ?? null,
30
- open: () => ctx.open,
26
+ const overlay = createAnchoredOverlayContent({
27
+ ctx,
31
28
  placement: () => placement,
32
- offset: () => offset
29
+ offset: () => offset,
30
+ style: () => style
33
31
  });
34
32
 
35
33
  createDismiss({
36
34
  enabled: () => ctx.open,
37
35
  onDismiss: () => ctx.close(),
38
- contentEl: () => contentEl ?? null,
36
+ contentEl: overlay.contentEl,
39
37
  triggerEl: () => ctx.triggerEl,
40
38
  preventDefaultOnEscape: true,
41
39
  returnFocusTo: () => ctx.triggerEl
@@ -43,12 +41,12 @@
43
41
  </script>
44
42
 
45
43
  <div
46
- bind:this={contentEl}
44
+ {@attach overlay.bindContent}
45
+ {@attach overlay.position}
47
46
  id={ctx.contentId}
48
47
  popover="manual"
49
48
  data-popover-content
50
49
  data-state={ctx.open ? 'open' : 'closed'}
51
- use:popover.applyPosition={style}
52
50
  class={className}
53
51
  {...rest}
54
52
  >
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { getWeekStartDay, addMonths } from '@dryui/primitives';
4
+ import { createDateViewController } from '../internal/date-family-controller.svelte.js';
5
5
  import { setRangeCalendarCtx } from './context.svelte.js';
6
6
 
7
7
  interface Props extends HTMLAttributes<HTMLDivElement> {
@@ -26,11 +26,11 @@
26
26
  ...rest
27
27
  }: Props = $props();
28
28
 
29
- const weekStartDay = $derived(getWeekStartDay(locale));
29
+ const view = createDateViewController({
30
+ initialDate: startDate,
31
+ locale: () => locale
32
+ });
30
33
 
31
- let viewMonth = $state(startDate ? startDate.getMonth() : new Date().getMonth());
32
- let viewYear = $state(startDate ? startDate.getFullYear() : new Date().getFullYear());
33
- let focusedDate = $state<Date>(startDate ?? new Date());
34
34
  let hoveredDate = $state<Date | null>(null);
35
35
 
36
36
  let selecting = $state(false);
@@ -46,13 +46,13 @@
46
46
  return hoveredDate;
47
47
  },
48
48
  get focusedDate() {
49
- return focusedDate;
49
+ return view.focusedDate;
50
50
  },
51
51
  get viewMonth() {
52
- return viewMonth;
52
+ return view.viewMonth;
53
53
  },
54
54
  get viewYear() {
55
- return viewYear;
55
+ return view.viewYear;
56
56
  },
57
57
  get locale() {
58
58
  return locale;
@@ -67,7 +67,7 @@
67
67
  return disabled;
68
68
  },
69
69
  get weekStartDay() {
70
- return weekStartDay;
70
+ return view.weekStartDay;
71
71
  },
72
72
  selectDate(date: Date) {
73
73
  if (!selecting) {
@@ -83,25 +83,19 @@
83
83
  }
84
84
  selecting = false;
85
85
  }
86
- focusedDate = date;
86
+ view.focusDate(date);
87
87
  },
88
88
  setHoveredDate(date: Date | null) {
89
89
  hoveredDate = date;
90
90
  },
91
91
  nextMonth() {
92
- const next = addMonths(new Date(viewYear, viewMonth, 1), 1);
93
- viewMonth = next.getMonth();
94
- viewYear = next.getFullYear();
92
+ view.nextMonth();
95
93
  },
96
94
  prevMonth() {
97
- const prev = addMonths(new Date(viewYear, viewMonth, 1), -1);
98
- viewMonth = prev.getMonth();
99
- viewYear = prev.getFullYear();
95
+ view.prevMonth();
100
96
  },
101
97
  setFocusedDate(date: Date) {
102
- focusedDate = date;
103
- viewMonth = date.getMonth();
104
- viewYear = date.getFullYear();
98
+ view.setFocusedDate(date);
105
99
  }
106
100
  });
107
101
  </script>
@@ -7,6 +7,7 @@ export interface TextProps extends HTMLAttributes<HTMLElement> {
7
7
  font?: 'sans' | 'mono';
8
8
  weight?: 'normal' | 'medium' | 'semibold' | 'bold';
9
9
  variant?: 'default' | 'label';
10
+ className?: HTMLAttributes<HTMLElement>['class'];
10
11
  children: Snippet;
11
12
  }
12
13
  export { default as Text } from './text.svelte';
@@ -10,6 +10,7 @@
10
10
  font?: 'sans' | 'mono';
11
11
  weight?: 'normal' | 'medium' | 'semibold' | 'bold';
12
12
  variant?: 'default' | 'label';
13
+ className?: HTMLAttributes<HTMLElement>['class'];
13
14
  children: Snippet;
14
15
  }
15
16
 
@@ -20,7 +21,8 @@
20
21
  font = 'sans',
21
22
  weight,
22
23
  variant = 'default',
23
- class: className,
24
+ class: classAttr,
25
+ className = classAttr,
24
26
  children,
25
27
  ...rest
26
28
  }: Props = $props();
@@ -7,6 +7,7 @@ interface Props extends HTMLAttributes<HTMLElement> {
7
7
  font?: 'sans' | 'mono';
8
8
  weight?: 'normal' | 'medium' | 'semibold' | 'bold';
9
9
  variant?: 'default' | 'label';
10
+ className?: HTMLAttributes<HTMLElement>['class'];
10
11
  children: Snippet;
11
12
  }
12
13
  declare const Text: import("svelte").Component<Props, {}, "">;
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassValue, HTMLButtonAttributes } from 'svelte/elements';
3
+ export { createThemeController } from './theme-controller.svelte.js';
4
+ export type { ThemeMode, ThemeController, ThemeControllerOptions } from './theme-controller.svelte.js';
5
+ export { themeFlashScript } from './theme-flash.js';
6
+ import type { ThemeController, ThemeMode } from './theme-controller.svelte.js';
7
+ export interface ThemeToggleProps extends Omit<HTMLButtonAttributes, 'onclick' | 'onkeydown' | 'disabled' | 'class'> {
8
+ storageKey?: string;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ controller?: ThemeController;
11
+ 'aria-label'?: string;
12
+ sunIcon?: Snippet;
13
+ moonIcon?: Snippet;
14
+ onModeChange?: (mode: ThemeMode) => void;
15
+ disabled?: boolean;
16
+ class?: ClassValue;
17
+ }
18
+ export { default as ThemeToggle } from './theme-toggle.svelte';