@dryui/ui 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/accordion/accordion-content.svelte +1 -1
  2. package/dist/alert/alert.svelte +1 -1
  3. package/dist/app-frame/app-frame.svelte +131 -0
  4. package/dist/app-frame/app-frame.svelte.d.ts +10 -0
  5. package/dist/app-frame/index.d.ts +8 -0
  6. package/dist/app-frame/index.js +1 -0
  7. package/dist/aurora/aurora.svelte +22 -59
  8. package/dist/beam/beam.svelte +28 -9
  9. package/dist/carousel/carousel-button-dots.svelte +25 -8
  10. package/dist/carousel/carousel-button-thumbnails.svelte +25 -8
  11. package/dist/carousel/carousel-root.svelte +115 -4
  12. package/dist/carousel/carousel-slide.svelte +5 -1
  13. package/dist/carousel/carousel-viewport.svelte +2 -0
  14. package/dist/carousel/context.svelte.d.ts +5 -0
  15. package/dist/chart/chart-bars.svelte +25 -16
  16. package/dist/chart/chart-donut.svelte +25 -16
  17. package/dist/chart/chart-root.svelte +134 -30
  18. package/dist/chart/chart-root.svelte.d.ts +1 -0
  19. package/dist/chart/context.svelte.d.ts +3 -1
  20. package/dist/chart/context.svelte.js +1 -0
  21. package/dist/chart/index.d.ts +1 -0
  22. package/dist/chromatic-shift/chromatic-shift.svelte +36 -9
  23. package/dist/collapsible/collapsible-content.svelte +2 -1
  24. package/dist/combobox/combobox-content.svelte +26 -44
  25. package/dist/combobox/combobox-content.svelte.d.ts +1 -1
  26. package/dist/combobox/combobox-input-root.svelte +7 -1
  27. package/dist/combobox/combobox-input.svelte +21 -8
  28. package/dist/country-select/country-select-button-input.svelte +124 -260
  29. package/dist/date-picker/datepicker-content.svelte +18 -26
  30. package/dist/date-picker/datepicker-content.svelte.d.ts +2 -1
  31. package/dist/date-picker/datepicker-input-root.svelte +7 -1
  32. package/dist/date-range-picker/date-range-picker-content.svelte +18 -14
  33. package/dist/date-range-picker/date-range-picker-content.svelte.d.ts +2 -1
  34. package/dist/date-range-picker/date-range-picker-root.svelte +7 -1
  35. package/dist/displacement/displacement.svelte +16 -22
  36. package/dist/drag-and-drop/context.svelte.d.ts +2 -0
  37. package/dist/drag-and-drop/drag-and-drop-handle.svelte +34 -5
  38. package/dist/drag-and-drop/drag-and-drop-item.svelte +23 -14
  39. package/dist/drag-and-drop/drag-and-drop-root.svelte +60 -16
  40. package/dist/god-rays/god-rays.svelte +11 -0
  41. package/dist/gradient-mesh/gradient-mesh.svelte +27 -5
  42. package/dist/hover-card/context.svelte.d.ts +1 -10
  43. package/dist/hover-card/context.svelte.js +1 -2
  44. package/dist/hover-card/hover-card-content.svelte +41 -3
  45. package/dist/hover-card/hover-card-root.svelte +7 -55
  46. package/dist/hover-card/hover-card-trigger.svelte +79 -40
  47. package/dist/hover-card/hover-card-trigger.svelte.d.ts +1 -1
  48. package/dist/index.d.ts +2 -0
  49. package/dist/index.js +1 -0
  50. package/dist/internal/motion.d.ts +1 -1
  51. package/dist/internal/motion.js +1 -1
  52. package/dist/marquee/marquee.svelte +42 -5
  53. package/dist/mega-menu/context.svelte.d.ts +2 -1
  54. package/dist/mega-menu/mega-menu-button-trigger.svelte +2 -14
  55. package/dist/mega-menu/mega-menu-item.svelte +3 -1
  56. package/dist/mega-menu/mega-menu-panel.svelte +35 -3
  57. package/dist/mega-menu/mega-menu-root.svelte +28 -13
  58. package/dist/menubar/context.svelte.d.ts +2 -2
  59. package/dist/menubar/menubar-button-trigger.svelte +5 -3
  60. package/dist/menubar/menubar-content.svelte +20 -12
  61. package/dist/menubar/menubar-root.svelte +4 -4
  62. package/dist/multi-select-combobox/multi-select-combobox-content.svelte +18 -55
  63. package/dist/multi-select-combobox/multi-select-combobox-content.svelte.d.ts +1 -1
  64. package/dist/noise/noise.svelte +38 -6
  65. package/dist/notification-center/context.svelte.d.ts +0 -1
  66. package/dist/notification-center/notification-center-panel.svelte +54 -35
  67. package/dist/notification-center/notification-center-root.svelte +0 -1
  68. package/dist/notification-center/notification-center-trigger-button.svelte +1 -8
  69. package/dist/option-picker/option-picker-description.svelte +2 -2
  70. package/dist/option-picker/option-picker-item.svelte +10 -3
  71. package/dist/option-picker/option-picker-label.svelte +2 -2
  72. package/dist/option-picker/option-picker-preview.svelte +18 -13
  73. package/dist/phone-input/phone-input-select.svelte +2 -152
  74. package/dist/phone-input/phone-input-select.svelte.d.ts +1 -7
  75. package/dist/rich-text-editor/rich-text-editor-toolbar-button-input.svelte +84 -29
  76. package/dist/scroll-area/scroll-area.svelte +16 -4
  77. package/dist/select/select-content.svelte +21 -31
  78. package/dist/select/select-content.svelte.d.ts +1 -1
  79. package/dist/select/select-root-input.svelte +7 -1
  80. package/dist/shimmer/shimmer.svelte +22 -12
  81. package/dist/transfer/transfer-item.svelte +0 -3
  82. package/dist/transfer/transfer-list-input.svelte +1 -6
  83. package/dist/tree/context.svelte.d.ts +7 -1
  84. package/dist/tree/tree-item-children.svelte +12 -10
  85. package/dist/tree/tree-item-label.svelte +6 -17
  86. package/dist/tree/tree-item-label.svelte.d.ts +2 -2
  87. package/dist/tree/tree-item.svelte +28 -1
  88. package/dist/tree/tree-root.svelte +135 -59
  89. package/package.json +8 -2
  90. package/skills/dryui/SKILL.md +1 -0
  91. package/dist/hover-card/hover-card-root.svelte.d.ts +0 -9
@@ -23,6 +23,38 @@
23
23
  }
24
24
 
25
25
  let expandedItems = createExpandedItems();
26
+ let branchItems = new SvelteSet<string>();
27
+ let focusedItem = $state<string | null>(selectedItem);
28
+
29
+ function toggleItem(id: string) {
30
+ if (expandedItems.has(id)) expandedItems.delete(id);
31
+ else expandedItems.add(id);
32
+ }
33
+
34
+ function expandItem(id: string) {
35
+ expandedItems.add(id);
36
+ }
37
+
38
+ function collapseItem(id: string) {
39
+ expandedItems.delete(id);
40
+ }
41
+
42
+ function selectItem(id: string) {
43
+ selectedItem = id;
44
+ focusedItem = id;
45
+ }
46
+
47
+ function setFocusedItem(id: string) {
48
+ focusedItem = id;
49
+ }
50
+
51
+ function registerBranch(id: string) {
52
+ branchItems.add(id);
53
+ }
54
+
55
+ function unregisterBranch(id: string) {
56
+ branchItems.delete(id);
57
+ }
26
58
 
27
59
  setTreeCtx({
28
60
  get expandedItems() {
@@ -31,115 +63,151 @@
31
63
  get selectedItem() {
32
64
  return selectedItem;
33
65
  },
34
- toggleItem(id) {
35
- if (expandedItems.has(id)) expandedItems.delete(id);
36
- else expandedItems.add(id);
37
- },
38
- expandItem(id) {
39
- expandedItems.add(id);
40
- },
41
- collapseItem(id) {
42
- expandedItems.delete(id);
43
- },
44
- selectItem(id) {
45
- selectedItem = id;
66
+ get focusedItem() {
67
+ return focusedItem;
46
68
  },
69
+ toggleItem,
70
+ expandItem,
71
+ collapseItem,
72
+ selectItem,
73
+ setFocusedItem,
74
+ registerBranch,
75
+ unregisterBranch,
47
76
  isExpanded(id) {
48
77
  return expandedItems.has(id);
49
78
  },
50
79
  isSelected(id) {
51
80
  return selectedItem === id;
81
+ },
82
+ isFocused(id) {
83
+ return focusedItem === id;
84
+ },
85
+ hasChildren(id) {
86
+ return branchItems.has(id);
52
87
  }
53
88
  });
54
89
 
55
- function handleKeydown(e: KeyboardEvent) {
56
- const tree = e.currentTarget as HTMLElement;
90
+ function initializeTree(node: HTMLElement) {
91
+ if (focusedItem !== null) return;
92
+ const itemId = node.querySelector<HTMLElement>('[role="treeitem"]')?.dataset.itemId;
93
+
94
+ if (itemId) {
95
+ focusedItem = itemId;
96
+ }
97
+ }
98
+
99
+ function getVisibleItems(tree: HTMLElement) {
57
100
  const items = Array.from(tree.querySelectorAll('[role="treeitem"]')) as HTMLElement[];
58
- const visibleItems = items.filter((item) => {
101
+
102
+ return items.filter((item) => {
59
103
  let parent = item.parentElement;
104
+
60
105
  while (parent && parent !== tree) {
61
106
  if (parent.getAttribute('role') === 'group') {
62
- const groupParent = parent.closest('[role="treeitem"]');
63
- if (groupParent && groupParent.getAttribute('aria-expanded') === 'false') {
107
+ const groupParent = parent.closest<HTMLElement>('[role="treeitem"]');
108
+ if (groupParent?.getAttribute('aria-expanded') === 'false') {
64
109
  return false;
65
110
  }
66
111
  }
112
+
67
113
  parent = parent.parentElement;
68
114
  }
115
+
69
116
  return true;
70
117
  });
118
+ }
71
119
 
72
- const currentIndex = visibleItems.indexOf(
73
- document.activeElement?.closest('[role="treeitem"]') as HTMLElement
74
- );
75
- if (currentIndex === -1) return;
120
+ function getCurrentItem(tree: HTMLElement, visibleItems: HTMLElement[]) {
121
+ const activeItem =
122
+ document.activeElement instanceof HTMLElement
123
+ ? document.activeElement.closest<HTMLElement>('[role="treeitem"]')
124
+ : null;
125
+
126
+ if (activeItem && tree.contains(activeItem)) {
127
+ return activeItem;
128
+ }
129
+
130
+ if (focusedItem !== null) {
131
+ return visibleItems.find((item) => item.dataset.itemId === focusedItem) ?? null;
132
+ }
76
133
 
77
- const currentItem = visibleItems[currentIndex]!;
134
+ return visibleItems[0] ?? null;
135
+ }
136
+
137
+ function focusItem(item: HTMLElement | null | undefined) {
138
+ if (!item) return;
139
+ const itemId = item.dataset.itemId;
140
+ if (itemId && itemId !== focusedItem) {
141
+ focusedItem = itemId;
142
+ }
143
+ item.focus();
144
+ }
145
+
146
+ function handleFocusIn(e: FocusEvent) {
147
+ const item =
148
+ e.target instanceof HTMLElement ? e.target.closest<HTMLElement>('[role="treeitem"]') : null;
149
+ const itemId = item?.dataset.itemId;
150
+
151
+ if (itemId && itemId !== focusedItem) {
152
+ focusedItem = itemId;
153
+ }
154
+ }
155
+
156
+ function handleKeydown(e: KeyboardEvent) {
157
+ const tree = e.currentTarget as HTMLElement;
158
+ const visibleItems = getVisibleItems(tree);
159
+ const currentItem = getCurrentItem(tree, visibleItems);
160
+ if (!currentItem) return;
161
+
162
+ const currentIndex = visibleItems.indexOf(currentItem);
78
163
  const itemId = currentItem.dataset.itemId;
79
164
 
80
165
  switch (e.key) {
81
166
  case 'ArrowDown': {
82
167
  e.preventDefault();
83
- const next = visibleItems[currentIndex + 1];
84
- if (next) {
85
- const label = next.querySelector('[data-tree-label]') as HTMLElement;
86
- (label ?? next).focus();
87
- }
168
+ focusItem(visibleItems[currentIndex + 1]);
88
169
  break;
89
170
  }
90
171
  case 'ArrowUp': {
91
172
  e.preventDefault();
92
- const prev = visibleItems[currentIndex - 1];
93
- if (prev) {
94
- const label = prev.querySelector('[data-tree-label]') as HTMLElement;
95
- (label ?? prev).focus();
96
- }
173
+ focusItem(visibleItems[currentIndex - 1]);
97
174
  break;
98
175
  }
99
176
  case 'ArrowRight': {
100
177
  e.preventDefault();
101
- if (itemId) {
102
- const hasChildren = currentItem.querySelector('[role="group"]');
103
- if (hasChildren && !expandedItems.has(itemId)) {
104
- expandedItems.add(itemId);
105
- } else if (hasChildren && expandedItems.has(itemId)) {
106
- const firstChild = currentItem.querySelector('[role="group"] > [role="treeitem"]');
107
- if (firstChild) {
108
- const label = firstChild.querySelector('[data-tree-label]') as HTMLElement;
109
- (label ?? (firstChild as HTMLElement)).focus();
110
- }
178
+ if (itemId && branchItems.has(itemId)) {
179
+ if (!expandedItems.has(itemId)) {
180
+ expandItem(itemId);
181
+ } else {
182
+ focusItem(currentItem.querySelector<HTMLElement>('[role="group"] [role="treeitem"]'));
111
183
  }
112
184
  }
113
185
  break;
114
186
  }
115
187
  case 'ArrowLeft': {
116
188
  e.preventDefault();
117
- if (itemId && expandedItems.has(itemId)) {
118
- expandedItems.delete(itemId);
189
+ if (itemId && branchItems.has(itemId) && expandedItems.has(itemId)) {
190
+ collapseItem(itemId);
119
191
  } else {
120
- const parentGroup = currentItem.parentElement?.closest('[role="treeitem"]');
121
- if (parentGroup) {
122
- const label = parentGroup.querySelector('[data-tree-label]') as HTMLElement;
123
- (label ?? (parentGroup as HTMLElement)).focus();
124
- }
192
+ focusItem(currentItem.parentElement?.closest<HTMLElement>('[role="treeitem"]'));
125
193
  }
126
194
  break;
127
195
  }
128
196
  case 'Home': {
129
197
  e.preventDefault();
130
- const first = visibleItems[0];
131
- if (first) {
132
- const label = first.querySelector('[data-tree-label]') as HTMLElement;
133
- (label ?? first).focus();
134
- }
198
+ focusItem(visibleItems[0]);
135
199
  break;
136
200
  }
137
201
  case 'End': {
138
202
  e.preventDefault();
139
- const last = visibleItems[visibleItems.length - 1];
140
- if (last) {
141
- const label = last.querySelector('[data-tree-label]') as HTMLElement;
142
- (label ?? last).focus();
203
+ focusItem(visibleItems[visibleItems.length - 1]);
204
+ break;
205
+ }
206
+ case 'Enter':
207
+ case ' ': {
208
+ if (itemId) {
209
+ e.preventDefault();
210
+ selectItem(itemId);
143
211
  }
144
212
  break;
145
213
  }
@@ -147,7 +215,15 @@
147
215
  }
148
216
  </script>
149
217
 
150
- <div role="tree" data-part="root" class={className} onkeydown={handleKeydown} {...rest}>
218
+ <div
219
+ role="tree"
220
+ data-part="root"
221
+ class={className}
222
+ onfocusin={handleFocusIn}
223
+ onkeydown={handleKeydown}
224
+ {@attach initializeTree}
225
+ {...rest}
226
+ >
151
227
  {@render children()}
152
228
  </div>
153
229
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dryui/ui",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "Zero-dependency styled Svelte 5 components with scoped styles and --dry-* CSS variable theming.",
5
5
  "author": "Rob Balfre",
6
6
  "license": "MIT",
@@ -50,6 +50,11 @@
50
50
  "svelte": "./dist/alpha-slider/index.js",
51
51
  "default": "./dist/alpha-slider/index.js"
52
52
  },
53
+ "./app-frame": {
54
+ "types": "./dist/app-frame/index.d.ts",
55
+ "svelte": "./dist/app-frame/index.js",
56
+ "default": "./dist/app-frame/index.js"
57
+ },
53
58
  "./aspect-ratio": {
54
59
  "types": "./dist/aspect-ratio/index.d.ts",
55
60
  "svelte": "./dist/aspect-ratio/index.js",
@@ -779,12 +784,13 @@
779
784
  "postpack": "bun ../../scripts/postpack-exports.ts"
780
785
  },
781
786
  "dependencies": {
782
- "@dryui/primitives": "^1.3.1"
787
+ "@dryui/primitives": "^1.4.1"
783
788
  },
784
789
  "peerDependencies": {
785
790
  "svelte": "^5.55.1"
786
791
  },
787
792
  "devDependencies": {
793
+ "@dryui/lint": "^0.4.2",
788
794
  "svelte": "^5.55.3",
789
795
  "@sveltejs/package": "^2.5.7",
790
796
  "svelte-check": "^4.4.6",
@@ -146,6 +146,7 @@ This works for greenfield (empty directory), brownfield (existing non-SvelteKit
146
146
 
147
147
  - Claude Code: `claude plugin marketplace add rob-balfre/dryui && claude plugin install dryui@dryui` (plugin is the canonical Claude skill install path)
148
148
  - Codex (0.121.0+): `codex marketplace add rob-balfre/dryui`, then start `codex`, run `/plugins`, and install `DryUI` (plugin is the canonical Codex skill install path)
149
+ - OpenCode: `npx degit rob-balfre/dryui/packages/ui/skills/dryui .opencode/skills/dryui` + add the `dryui` and `dryui-feedback` local MCP servers in `opencode.json` (OpenCode also loads `.agents/skills/dryui` and reads `AGENTS.md`)
149
150
  - Copilot/Cursor/Windsurf: `npx degit rob-balfre/dryui/packages/ui/skills/dryui .agents/skills/dryui` + add MCP config (see https://dryui.dev/tools)
150
151
 
151
152
  ### Manual setup
@@ -1,9 +0,0 @@
1
- import type { Snippet } from 'svelte';
2
- interface Props {
3
- openDelay?: number;
4
- closeDelay?: number;
5
- children: Snippet;
6
- }
7
- declare const HoverCardRoot: import("svelte").Component<Props, {}, "">;
8
- type HoverCardRoot = ReturnType<typeof HoverCardRoot>;
9
- export default HoverCardRoot;