@destyler-ui/svelte 0.0.1-beta.17 → 0.0.1-beta.21

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 (97) hide show
  1. package/README.md +7 -1
  2. package/dist/components/aspect-ratio/components/Root.svelte +1 -2
  3. package/dist/components/aspect-ratio/hooks/use-aspect-ratio.svelte.d.ts +0 -1
  4. package/dist/components/aspect-ratio/hooks/use-aspect-ratio.svelte.js +1 -1
  5. package/dist/components/calendar/anatomy.d.ts +1 -1
  6. package/dist/components/calendar/components/Content.d.svelte.ts +1 -1
  7. package/dist/components/calendar/components/Content.svelte +2 -3
  8. package/dist/components/calendar/components/Content.svelte.d.ts +1 -1
  9. package/dist/components/calendar/components/Root.svelte +14 -8
  10. package/dist/components/calendar/hooks/use-calendar.svelte.d.ts +1 -1
  11. package/dist/components/calendar/hooks/use-calendar.svelte.js +13 -3
  12. package/dist/components/carousel/components/Root.svelte +9 -3
  13. package/dist/components/checkbox/components/Group.svelte +8 -2
  14. package/dist/components/checkbox/components/Root.svelte +8 -2
  15. package/dist/components/checkbox/hooks/use-checkbox-group.svelte.d.ts +17 -7
  16. package/dist/components/checkbox/hooks/use-checkbox-group.svelte.js +7 -4
  17. package/dist/components/checkbox/hooks/use-checkbox.svelte.js +14 -2
  18. package/dist/components/collapse/components/Root.svelte +9 -3
  19. package/dist/components/collapsible/components/Root.svelte +9 -3
  20. package/dist/components/collapsible/hooks/split-collapsible-props.svelte.d.ts +1 -1
  21. package/dist/components/collapsible/hooks/use-collapsible.svelte.d.ts +1 -1
  22. package/dist/components/color-picker/anatomy.d.ts +1 -1
  23. package/dist/components/color-picker/components/ChannelInput.svelte +9 -1
  24. package/dist/components/color-picker/components/Root.svelte +12 -4
  25. package/dist/components/color-picker/hooks/split-color-picker-props.svelte.d.ts +1 -1
  26. package/dist/components/color-picker/hooks/use-color-picker.svelte.d.ts +1 -1
  27. package/dist/components/combobox/components/Content.svelte +4 -7
  28. package/dist/components/combobox/components/Root.svelte +12 -6
  29. package/dist/components/combobox/hooks/use-combobox.svelte.d.ts +1 -1
  30. package/dist/components/combobox/hooks/use-combobox.svelte.js +13 -1
  31. package/dist/components/dialog/components/Root.svelte +10 -3
  32. package/dist/components/dialog/hooks/use-dialog.svelte.d.ts +1 -1
  33. package/dist/components/dynamic/components/Root.svelte +11 -3
  34. package/dist/components/dynamic/hooks/use-dynamic.svelte.js +26 -13
  35. package/dist/components/edit/components/Root.svelte +12 -5
  36. package/dist/components/edit/hooks/use-edit.svelte.d.ts +1 -1
  37. package/dist/components/edit/hooks/use-edit.svelte.js +25 -3
  38. package/dist/components/floating-panel/components/Root.svelte +17 -7
  39. package/dist/components/floating-panel/hooks/use-floating-panel.svelte.d.ts +1 -1
  40. package/dist/components/floating-panel/hooks/use-floating-panel.svelte.js +0 -5
  41. package/dist/components/hover-card/components/Root.svelte +9 -3
  42. package/dist/components/hover-card/hooks/use-hover-card.svelte.d.ts +1 -1
  43. package/dist/components/menu/components/CheckboxItem.svelte +9 -3
  44. package/dist/components/menu/components/RadioItemGroup.svelte +5 -2
  45. package/dist/components/menu/components/Root.svelte +9 -3
  46. package/dist/components/menu/hooks/use-menu.svelte.d.ts +1 -1
  47. package/dist/components/navigation-menu/components/Root.svelte +3 -3
  48. package/dist/components/navigation-menu/hooks/connect-navigation-menu.d.ts +10 -0
  49. package/dist/components/navigation-menu/hooks/connect-navigation-menu.js +42 -0
  50. package/dist/components/navigation-menu/hooks/use-navigation-menu.svelte.d.ts +1 -2
  51. package/dist/components/navigation-menu/hooks/use-navigation-menu.svelte.js +11 -21
  52. package/dist/components/number-input/components/Root.svelte +8 -2
  53. package/dist/components/number-input/hooks/use-number-input.svelte.js +183 -2
  54. package/dist/components/otp-input/components/Input.svelte +6 -1
  55. package/dist/components/otp-input/components/Root.svelte +8 -2
  56. package/dist/components/pagination/components/Root.svelte +14 -5
  57. package/dist/components/popover/components/Root.svelte +10 -3
  58. package/dist/components/popover/hooks/use-popover.svelte.d.ts +1 -1
  59. package/dist/components/presence/components/Presence.svelte +6 -0
  60. package/dist/components/progress/components/Root.svelte +8 -2
  61. package/dist/components/progress/hooks/use-progress.svelte.js +17 -3
  62. package/dist/components/qr-code/components/Root.svelte +8 -2
  63. package/dist/components/qr-code/hooks/use-qr-code.svelte.js +14 -4
  64. package/dist/components/radio/components/Root.svelte +9 -3
  65. package/dist/components/radio/hooks/use-radio.svelte.js +10 -1
  66. package/dist/components/scroll-area/hooks/use-scroll-area.svelte.js +0 -28
  67. package/dist/components/select/components/Root.svelte +7 -3
  68. package/dist/components/select/hooks/use-select.svelte.d.ts +1 -1
  69. package/dist/components/slider/components/DraggingIndicator.svelte +3 -3
  70. package/dist/components/slider/components/Root.svelte +8 -2
  71. package/dist/components/slider/components/ThumbPropsProvider.svelte +4 -2
  72. package/dist/components/splitter/components/Root.svelte +9 -3
  73. package/dist/components/steps/components/Root.svelte +9 -3
  74. package/dist/components/steps/hooks/split-steps-props.svelte.d.ts +1 -1
  75. package/dist/components/switch/components/Root.svelte +9 -3
  76. package/dist/components/switch/hooks/use-switch.svelte.d.ts +12 -1
  77. package/dist/components/switch/hooks/use-switch.svelte.js +10 -1
  78. package/dist/components/tabs/components/Root.svelte +9 -3
  79. package/dist/components/toggle/components/Root.svelte +8 -2
  80. package/dist/components/toggle-group/components/Root.svelte +9 -3
  81. package/dist/components/tooltip/components/Root.svelte +9 -3
  82. package/dist/components/tooltip/hooks/use-tooltip.svelte.d.ts +1 -1
  83. package/dist/components/tree/components/BranchTrigger.d.svelte.ts +2 -2
  84. package/dist/components/tree/components/BranchTrigger.svelte +3 -3
  85. package/dist/components/tree/components/BranchTrigger.svelte.d.ts +2 -2
  86. package/dist/components/tree/components/Root.svelte +8 -3
  87. package/dist/components/tree/hooks/tree-split-props.d.ts +1 -1
  88. package/dist/providers/frame/components/Content.svelte +4 -1
  89. package/dist/utils/create-machine-props.d.ts +18 -7
  90. package/dist/utils/create-machine-props.js +22 -18
  91. package/dist/utils/sync-checked.d.ts +6 -0
  92. package/dist/utils/sync-checked.js +28 -0
  93. package/dist/utils/sync-input-value.d.ts +4 -0
  94. package/dist/utils/sync-input-value.js +27 -0
  95. package/dist/utils/sync-number-input.d.ts +2 -0
  96. package/dist/utils/sync-number-input.js +45 -0
  97. package/package.json +72 -68
@@ -17,7 +17,14 @@
17
17
  import { DialogProvider } from '../hooks/use-dialog-context'
18
18
  import { useDialog } from '../hooks/use-dialog.svelte'
19
19
 
20
- let { open = $bindable(), children, ...props }: DialogRootProps = $props()
20
+ // Read the parent prop independently from the writable binding. Local writes
21
+ // notify bind: consumers without overriding ordinary controlled props.
22
+ let {
23
+ open = $bindable(),
24
+ open: openProp,
25
+ children,
26
+ ...props
27
+ }: DialogRootProps = $props()
21
28
 
22
29
  const providedId = $props.id()
23
30
 
@@ -28,10 +35,10 @@
28
35
  return {
29
36
  ...localProps,
30
37
  id: localProps.id ?? providedId,
31
- open,
38
+ open: openProp,
32
39
  onOpenChange(details) {
33
40
  localProps.onOpenChange?.(details)
34
- if (open !== undefined) open = details.open
41
+ open = details.open
35
42
  },
36
43
  }
37
44
  })
@@ -2,7 +2,7 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as dialog from '@destyler/dialog';
5
- export interface UseDialogProps extends Omit<dialog.Context, 'getRootNode' | 'dir' | 'open.controlled' | 'id'> {
5
+ export interface UseDialogProps extends Omit<dialog.Context, 'getRootNode' | 'dir' | 'id'> {
6
6
  id: string;
7
7
  defaultOpen?: dialog.Context['open'];
8
8
  }
@@ -13,7 +13,15 @@
13
13
  import { DynamicProvider } from '../hooks/use-dynamic-context'
14
14
  import { useDynamic } from '../hooks/use-dynamic.svelte'
15
15
 
16
- let { value = $bindable<string[]>(), inputValue = $bindable<string>(), ...props }: DynamicRootProps = $props()
16
+ // Read the parent prop independently from the writable binding. Local writes
17
+ // notify bind: consumers without overriding ordinary controlled props.
18
+ let {
19
+ value = $bindable<string[]>(),
20
+ value: valueProp,
21
+ inputValue = $bindable<string>(),
22
+ inputValue: inputValueProp,
23
+ ...props
24
+ }: DynamicRootProps = $props()
17
25
 
18
26
  const [useDynamicProps, localProps] = $derived(
19
27
  createSplitProps<Optional<UseDynamicProps, 'id'>>()(props, [
@@ -54,8 +62,8 @@
54
62
  return {
55
63
  ...useDynamicProps,
56
64
  id: useDynamicProps.id ?? providedId,
57
- value,
58
- inputValue,
65
+ value: valueProp,
66
+ inputValue: inputValueProp,
59
67
  onValueChange: (details) => {
60
68
  useDynamicProps.onValueChange?.(details)
61
69
  value = details.value
@@ -13,19 +13,32 @@ export function useDynamic(inProps) {
13
13
  const env = useEnvironmentContext();
14
14
  const locale = useLocaleContext();
15
15
  const field = useFieldContext();
16
- const machineProps = $derived.by(() => createMachineProps({
17
- ids: {
18
- label: field?.().ids.label,
19
- hiddenInput: field?.().ids.control,
20
- },
21
- dir: locale().dir,
22
- disabled: field?.().disabled,
23
- invalid: field?.().invalid,
24
- readOnly: field?.().readOnly,
25
- required: field?.().required,
26
- getRootNode: env().getRootNode,
27
- ...props,
28
- }, { value: 'defaultValue' }));
16
+ const machineProps = $derived.by(() => {
17
+ const { defaultValue, value, ...rest } = props;
18
+ const reactive = createMachineProps({
19
+ ids: {
20
+ label: field?.().ids.label,
21
+ hiddenInput: field?.().ids.control,
22
+ },
23
+ dir: locale().dir,
24
+ disabled: field?.().disabled,
25
+ invalid: field?.().invalid,
26
+ readOnly: field?.().readOnly,
27
+ required: field?.().required,
28
+ getRootNode: env().getRootNode,
29
+ ...rest,
30
+ ...(value !== undefined ? { value } : {}),
31
+ });
32
+ // @destyler/dynamic@0.2.7 stores value directly — seed defaults only once.
33
+ const initialValue = value !== undefined ? value : defaultValue;
34
+ return {
35
+ initial: {
36
+ ...reactive.initial,
37
+ ...(initialValue !== undefined ? { value: initialValue } : {}),
38
+ },
39
+ context: reactive.context,
40
+ };
41
+ });
29
42
  const [state, send] = useMachine(() => dynamic.machine(machineProps.initial), {
30
43
  get context() {
31
44
  return machineProps.context;
@@ -13,7 +13,15 @@
13
13
  import { EditProvider } from '../hooks/use-edit-context'
14
14
  import { useEdit } from '../hooks/use-edit.svelte'
15
15
 
16
- let { value = $bindable(), edit = $bindable(), ...props }: EditRootProps = $props()
16
+ // Read the parent prop independently from the writable binding. Local writes
17
+ // notify bind: consumers without overriding ordinary controlled props.
18
+ let {
19
+ value = $bindable(),
20
+ value: valueProp,
21
+ edit = $bindable(),
22
+ edit: editProp,
23
+ ...props
24
+ }: EditRootProps = $props()
17
25
 
18
26
  const providedId = $props.id()
19
27
 
@@ -23,16 +31,15 @@
23
31
  return {
24
32
  ...useEditProps,
25
33
  id: useEditProps.id ?? providedId,
26
- value,
27
- edit,
34
+ value: valueProp,
35
+ edit: editProp,
28
36
  onValueChange(details) {
29
37
  useEditProps.onValueChange?.(details)
30
38
  value = details.value
31
39
  },
32
40
  onEditChange(details) {
33
41
  useEditProps.onEditChange?.(details)
34
- if (edit !== undefined)
35
- edit = details.edit
42
+ edit = details.edit
36
43
  },
37
44
  }
38
45
  })
@@ -2,7 +2,7 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as edit from '@destyler/edit';
5
- export interface UseEditProps extends Omit<edit.Context, 'dir' | 'getRootNode' | 'edit.controlled' | 'id'> {
5
+ export interface UseEditProps extends Omit<edit.Context, 'dir' | 'getRootNode' | 'id'> {
6
6
  id: string;
7
7
  defaultEdit?: edit.Context['edit'];
8
8
  defaultValue?: edit.Context['value'];
@@ -3,15 +3,21 @@ import { useEnvironmentContext } from '../../../providers/environment/index.js';
3
3
  import { useLocaleContext } from '../../../providers/locale/index.js';
4
4
  import { createMachineProps } from '../../../utils/create-machine-props.js';
5
5
  import { normalizeProps } from '../../../utils/normalize-props.js';
6
+ import { createInputValueSync } from '../../../utils/sync-input-value.js';
6
7
  import * as edit from '@destyler/edit';
8
+ import { mergeProps } from '@destyler/svelte';
7
9
  import { runIfFn } from '@destyler/utils';
10
+ import { untrack } from 'svelte';
8
11
  import { useFieldContext } from '../../field/index.js';
9
12
  export function useEdit(props) {
10
13
  const env = useEnvironmentContext();
11
14
  const locale = useLocaleContext();
12
15
  const field = useFieldContext();
16
+ // Core has no defaultEdit. The UI owns this state and writes edit requests back
17
+ // to core, while an explicit live edit prop remains owned by the caller.
18
+ let localEdit = $state(untrack(() => runIfFn(props).defaultEdit ?? false));
13
19
  const machineProps = $derived.by(() => {
14
- const resolvedProps = runIfFn(props) || {};
20
+ const { defaultEdit: _defaultEdit, edit: liveEdit, onEditChange, ...resolvedProps } = runIfFn(props);
15
21
  return createMachineProps({
16
22
  getRootNode: env().getRootNode,
17
23
  dir: locale().dir,
@@ -24,13 +30,29 @@ export function useEdit(props) {
24
30
  readOnly: field?.()?.readOnly,
25
31
  required: field?.()?.required,
26
32
  ...resolvedProps,
27
- }, { edit: 'defaultEdit', value: 'defaultValue' }, ['edit']);
33
+ edit: liveEdit !== undefined ? liveEdit : localEdit,
34
+ onEditChange(details) {
35
+ if (liveEdit === undefined)
36
+ localEdit = details.edit;
37
+ onEditChange?.(details);
38
+ },
39
+ }, { value: 'defaultValue' });
28
40
  });
29
41
  const [state, send] = useMachine(() => edit.machine(machineProps.initial), {
30
42
  get context() {
31
43
  return machineProps.context;
32
44
  },
33
45
  });
34
- const api = $derived(edit.connect(state, send, normalizeProps));
46
+ const connected = $derived(edit.connect(state, send, normalizeProps));
47
+ const syncInputValue = createInputValueSync(() => connected.getInputProps(), () => connected.value);
48
+ const api = $derived({
49
+ ...connected,
50
+ getInputProps() {
51
+ return mergeProps(connected.getInputProps(), {
52
+ oninput: syncInputValue,
53
+ oncompositionend: syncInputValue,
54
+ });
55
+ },
56
+ });
35
57
  return () => api;
36
58
  }
@@ -18,7 +18,17 @@
18
18
  import { FloatingPanelProvider } from '../hooks/use-floating-panel-context.js'
19
19
  import { useFloatingPanel } from '../hooks/use-floating-panel.svelte.js'
20
20
 
21
- let { open = $bindable(), position = $bindable(), size = $bindable(), ...props }: FloatingPanelRootProps = $props()
21
+ // Read the parent prop independently from the writable binding. Local writes
22
+ // notify bind: consumers without overriding ordinary controlled props.
23
+ let {
24
+ open = $bindable(),
25
+ open: openProp,
26
+ position = $bindable(),
27
+ position: positionProp,
28
+ size = $bindable(),
29
+ size: sizeProp,
30
+ ...props
31
+ }: FloatingPanelRootProps = $props()
22
32
  const providedId = $props.id()
23
33
 
24
34
  const [presenceProps, otherProps] = $derived(splitPresenceProps(props))
@@ -53,20 +63,20 @@
53
63
  const resolvedProps = $derived<UseFloatingPanelProps>({
54
64
  ...floatingPanelProps,
55
65
  id: floatingPanelProps.id ?? providedId,
56
- open,
66
+ open: openProp,
57
67
  onOpenChange(details) {
58
68
  floatingPanelProps.onOpenChange?.(details)
59
- if (open !== undefined) open = details.open
69
+ open = details.open
60
70
  },
61
- position,
71
+ position: positionProp,
62
72
  onPositionChange(details) {
63
73
  floatingPanelProps.onPositionChange?.(details)
64
- if (position !== undefined) position = details.position
74
+ position = details.position
65
75
  },
66
- size,
76
+ size: sizeProp,
67
77
  onSizeChange(details) {
68
78
  floatingPanelProps.onSizeChange?.(details)
69
- if (size !== undefined) size = details.size
79
+ size = details.size
70
80
  },
71
81
  })
72
82
 
@@ -2,7 +2,7 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as floatingPanel from '@destyler/floating-panel';
5
- export interface UseFloatingPanelProps extends Omit<floatingPanel.Context, 'dir' | 'getRootNode' | 'open.controlled' | 'id'> {
5
+ export interface UseFloatingPanelProps extends Omit<floatingPanel.Context, 'dir' | 'getRootNode' | 'id'> {
6
6
  id: string;
7
7
  defaultOpen?: floatingPanel.Context['open'];
8
8
  }
@@ -21,10 +21,5 @@ export function useFloatingPanel(props) {
21
21
  },
22
22
  });
23
23
  const api = $derived(floatingPanel.connect(state, send, normalizeProps));
24
- $effect(() => {
25
- const open = runIfFn(props).open;
26
- if (open !== undefined && open !== api.open)
27
- api.setOpen(open);
28
- });
29
24
  return () => api;
30
25
  }
@@ -16,7 +16,13 @@
16
16
  import { HoverCardProvider } from '../hooks/use-hover-card-context'
17
17
  import { useHoverCard } from '../hooks/use-hover-card.svelte'
18
18
 
19
- let { open = $bindable<boolean>(), ...props }: HoverCardRootProps = $props()
19
+ // Read the parent prop independently from the writable binding. Local writes
20
+ // notify bind: consumers without overriding ordinary controlled props.
21
+ let {
22
+ open = $bindable<boolean>(),
23
+ open: openProp,
24
+ ...props
25
+ }: HoverCardRootProps = $props()
20
26
  const providedId = $props.id()
21
27
 
22
28
  const [presenceProps, localProps] = $derived(splitPresenceProps(props))
@@ -24,10 +30,10 @@
24
30
  const resolvedProps = $derived<UseHoverCardProps>({
25
31
  ...localProps,
26
32
  id: localProps.id ?? providedId,
27
- open,
33
+ open: openProp,
28
34
  onOpenChange(details) {
29
35
  localProps.onOpenChange?.(details)
30
- if (open !== undefined) open = details.open
36
+ open = details.open
31
37
  },
32
38
  })
33
39
 
@@ -2,7 +2,7 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as hoverCard from '@destyler/hover-card';
5
- export interface UseHoverCardProps extends Omit<hoverCard.Context, 'dir' | 'getRootNode' | 'open.controlled' | 'id'> {
5
+ export interface UseHoverCardProps extends Omit<hoverCard.Context, 'dir' | 'getRootNode' | 'id'> {
6
6
  id: string;
7
7
  defaultOpen?: hoverCard.Context['open'];
8
8
  }
@@ -17,7 +17,13 @@
17
17
  import { MenuItemProvider } from '../hooks/use-menu-item-context'
18
18
  import { MenuItemPropsProvider } from '../hooks/use-menu-option-item-props-context'
19
19
 
20
- let { checked = $bindable<boolean>(), ...props }: MenuCheckboxItemProps = $props()
20
+ // Read the parent prop independently from the writable binding. Local writes
21
+ // notify bind: consumers without overriding ordinary controlled props.
22
+ let {
23
+ checked = $bindable<boolean>(),
24
+ checked: checkedProp,
25
+ ...props
26
+ }: MenuCheckboxItemProps = $props()
21
27
 
22
28
  const [partialOptionItemProps, localProps] = $derived(
23
29
  createSplitProps<CheckboxMachineProps>()(props, [
@@ -32,9 +38,9 @@
32
38
  const optionItemProps = $derived<OptionItemProps>({
33
39
  ...partialOptionItemProps,
34
40
  type: 'checkbox',
35
- checked,
41
+ checked: checkedProp,
36
42
  onCheckedChange(nextChecked) {
37
- if (checked !== undefined) checked = nextChecked
43
+ checked = nextChecked
38
44
  partialOptionItemProps.onCheckedChange?.(nextChecked)
39
45
  },
40
46
  })
@@ -14,9 +14,12 @@
14
14
  import { useRequiredMenuContext } from '../hooks/use-menu-context'
15
15
  import { MenuItemGroupProvider, type ValueChangeDetails } from '../hooks/use-menu-item-group-context'
16
16
 
17
+ // Read the parent prop independently from the writable binding. Local writes
18
+ // notify bind: consumers without overriding ordinary controlled props.
17
19
  let {
18
20
  id: providedId,
19
21
  value = $bindable<string>(),
22
+ value: valueProp,
20
23
  onValueChange,
21
24
  ...localProps
22
25
  }: MenuRadioItemGroupProps = $props()
@@ -25,9 +28,9 @@
25
28
  const menu = useRequiredMenuContext()
26
29
  const itemGroupProps = $derived({
27
30
  id: providedId ?? generatedId,
28
- value,
31
+ value: valueProp,
29
32
  onValueChange(details: ValueChangeDetails) {
30
- if (value !== undefined) value = details.value
33
+ value = details.value
31
34
  onValueChange?.(details)
32
35
  },
33
36
  })
@@ -19,7 +19,13 @@
19
19
  import { MenuTriggerItemProvider } from '../hooks/use-menu-trigger-item-context'
20
20
  import { useMenu } from '../hooks/use-menu.svelte'
21
21
 
22
- let { open = $bindable<boolean>(), ...props }: MenuRootProps = $props()
22
+ // Read the parent prop independently from the writable binding. Local writes
23
+ // notify bind: consumers without overriding ordinary controlled props.
24
+ let {
25
+ open = $bindable<boolean>(),
26
+ open: openProp,
27
+ ...props
28
+ }: MenuRootProps = $props()
23
29
  const providedId = $props.id()
24
30
 
25
31
  const [presenceProps, menuProps] = $derived(splitPresenceProps(props))
@@ -54,10 +60,10 @@
54
60
  const resolvedProps = $derived<UseMenuProps>({
55
61
  ...useMenuProps,
56
62
  id: useMenuProps.id ?? providedId,
57
- open,
63
+ open: openProp,
58
64
  onOpenChange(details) {
59
65
  useMenuProps.onOpenChange?.(details)
60
- if (open !== undefined) open = details.open
66
+ open = details.open
61
67
  },
62
68
  })
63
69
 
@@ -2,7 +2,7 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as menu from '@destyler/menu';
5
- export interface UseMenuProps extends Omit<menu.Context, 'dir' | 'getRootNode' | 'open.controlled' | 'id'> {
5
+ export interface UseMenuProps extends Omit<menu.Context, 'dir' | 'getRootNode' | 'id'> {
6
6
  id: string;
7
7
  defaultOpen?: menu.Context['open'];
8
8
  }
@@ -16,7 +16,7 @@
16
16
  import { PresenceProvider, splitPresenceProps, usePresence } from '../../presence'
17
17
  import { createSplitProps } from '../../../utils/create-split-props'
18
18
 
19
- let { ref = $bindable(null), value = $bindable(), ...props }: NavigationMenuRootProps = $props()
19
+ let { ref = $bindable(null), value: valueProp, value = $bindable(), ...props }: NavigationMenuRootProps = $props()
20
20
 
21
21
  const providedId = $props.id()
22
22
  const splitRootProps = createSplitProps<Optional<UseNavigationMenuProps, 'id'>>()
@@ -41,10 +41,10 @@
41
41
  const machineProps = $derived<UseNavigationMenuProps>({
42
42
  ...useNavigationMenuProps,
43
43
  id: useNavigationMenuProps.id ?? providedId,
44
- value,
44
+ value: valueProp,
45
45
  onValueChange(details) {
46
46
  useNavigationMenuProps.onValueChange?.(details)
47
- if (value !== undefined) value = details.value
47
+ value = details.value
48
48
  },
49
49
  })
50
50
 
@@ -0,0 +1,10 @@
1
+ import type { PropTypes } from '@destyler/svelte';
2
+ import * as navigationMenu from '@destyler/navigation-menu';
3
+ type ConnectParameters = Parameters<typeof navigationMenu.connect<PropTypes>>;
4
+ /**
5
+ * Core 0.2.7 reads the trigger DOM to calculate content motion during render.
6
+ * Before mount (including hydration), return the same static content contract
7
+ * without a layout query. Keep the real core API for all mounted behavior.
8
+ */
9
+ export declare function connectNavigationMenu(state: ConnectParameters[0], send: ConnectParameters[1], mounted: boolean): navigationMenu.Api<PropTypes>;
10
+ export {};
@@ -0,0 +1,42 @@
1
+ import { normalizeProps } from '../../../utils/normalize-props.js';
2
+ import * as navigationMenu from '@destyler/navigation-menu';
3
+ /**
4
+ * Core 0.2.7 reads the trigger DOM to calculate content motion during render.
5
+ * Before mount (including hydration), return the same static content contract
6
+ * without a layout query. Keep the real core API for all mounted behavior.
7
+ */
8
+ export function connectNavigationMenu(state, send, mounted) {
9
+ const api = navigationMenu.connect(state, send, normalizeProps);
10
+ if (mounted)
11
+ return api;
12
+ const getContentState = props => ({
13
+ open: api.value === props.value,
14
+ motion: undefined,
15
+ });
16
+ return {
17
+ ...api,
18
+ getContentState,
19
+ getContentProps(props) {
20
+ const { value } = props;
21
+ const { open } = getContentState(props);
22
+ return normalizeProps.element({
23
+ ...navigationMenu.anatomy.build().content.attrs,
24
+ 'id': state.context.ids?.content?.(value) ?? `nav-menu:${state.context.id}:content:${value}`,
25
+ 'dir': state.context.dir,
26
+ 'hidden': !open,
27
+ 'data-state': open ? 'open' : 'closed',
28
+ 'data-motion': undefined,
29
+ 'data-value': value,
30
+ 'data-orientation': state.context.orientation,
31
+ onPointerEnter(event) {
32
+ if (event.pointerType === 'mouse')
33
+ send({ type: 'CONTENT_ENTER', value });
34
+ },
35
+ onPointerLeave(event) {
36
+ if (event.pointerType === 'mouse')
37
+ send({ type: 'CONTENT_LEAVE', value });
38
+ },
39
+ });
40
+ },
41
+ };
42
+ }
@@ -2,9 +2,8 @@ import type { Accessor } from '../../../types.js';
2
2
  import type { PropTypes } from '@destyler/svelte';
3
3
  import type { MaybeFunction } from '@destyler/utils';
4
4
  import * as navigationMenu from '@destyler/navigation-menu';
5
- export interface UseNavigationMenuProps extends Omit<navigationMenu.Context, 'dir' | 'getRootNode' | 'defaultValue' | 'value.controlled' | 'id'> {
5
+ export interface UseNavigationMenuProps extends Omit<navigationMenu.Context, 'dir' | 'getRootNode' | 'id'> {
6
6
  id: string;
7
- defaultValue?: string;
8
7
  }
9
8
  export interface UseNavigationMenuReturn extends Accessor<navigationMenu.Api<PropTypes>> {
10
9
  }
@@ -1,42 +1,32 @@
1
1
  import { useMachine } from '../../../hooks/use-destyler-machine.svelte.js';
2
2
  import { useEnvironmentContext } from '../../../providers/environment/index.js';
3
3
  import { useLocaleContext } from '../../../providers/locale/index.js';
4
- import { normalizeProps } from '../../../utils/normalize-props.js';
4
+ import { createMachineProps } from '../../../utils/create-machine-props.js';
5
5
  import * as navigationMenu from '@destyler/navigation-menu';
6
6
  import { runIfFn } from '@destyler/utils';
7
+ import { connectNavigationMenu } from './connect-navigation-menu.js';
7
8
  export function useNavigationMenu(props) {
9
+ let mounted = $state(false);
10
+ $effect(() => {
11
+ mounted = true;
12
+ });
8
13
  const env = useEnvironmentContext();
9
14
  const locale = useLocaleContext();
10
15
  const machineProps = $derived.by(() => {
11
16
  const resolvedProps = runIfFn(props) || {};
12
- const baseProps = {
17
+ return createMachineProps({
13
18
  dir: locale().dir,
14
19
  getRootNode: env().getRootNode,
15
20
  ...resolvedProps,
16
- };
17
- const controlled = resolvedProps.value !== undefined;
18
- const initialValue = controlled ? resolvedProps.value : (resolvedProps.defaultValue ?? null);
19
- const initialDefaultValue = controlled ? (resolvedProps.value ?? undefined) : resolvedProps.defaultValue;
20
- return {
21
- initial: {
22
- ...baseProps,
23
- 'defaultValue': initialDefaultValue,
24
- 'value': initialValue,
25
- 'value.controlled': controlled,
26
- },
27
- context: {
28
- ...baseProps,
29
- 'defaultValue': initialDefaultValue,
30
- 'value': resolvedProps.value,
31
- 'value.controlled': controlled,
32
- },
33
- };
21
+ // null explicitly closes the menu; only undefined permits the default seed.
22
+ defaultValue: resolvedProps.value !== undefined ? undefined : resolvedProps.defaultValue,
23
+ });
34
24
  });
35
25
  const [state, send] = useMachine(() => navigationMenu.machine(machineProps.initial), {
36
26
  get context() {
37
27
  return machineProps.context;
38
28
  },
39
29
  });
40
- const api = $derived(navigationMenu.connect(state, send, normalizeProps));
30
+ const api = $derived(connectNavigationMenu(state, send, mounted));
41
31
  return () => api;
42
32
  }
@@ -15,7 +15,13 @@
15
15
  import { splitNumberInputProps } from '../hooks/split-number-input-props.svelte'
16
16
  import { useNumberInput } from '../hooks/use-number-input.svelte'
17
17
 
18
- let { value = $bindable(), ...props }: NumberInputRootProps = $props()
18
+ // Read the parent prop independently from the writable binding. Local writes
19
+ // notify bind: consumers without overriding ordinary controlled props.
20
+ let {
21
+ value = $bindable(),
22
+ value: valueProp,
23
+ ...props
24
+ }: NumberInputRootProps = $props()
19
25
  const providedId = $props.id()
20
26
 
21
27
  const [useNumberInputProps, localProps] = $derived(splitNumberInputProps(props))
@@ -23,7 +29,7 @@
23
29
  const resolvedProps = $derived<UseNumberInputProps>({
24
30
  ...useNumberInputProps,
25
31
  id: useNumberInputProps.id ?? providedId,
26
- value,
32
+ value: valueProp,
27
33
  onValueChange(details) {
28
34
  useNumberInputProps.onValueChange?.(details)
29
35
  value = details.value