@dryui/ui 1.3.1 → 1.4.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 (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 +125 -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
@@ -19,13 +19,14 @@
19
19
 
20
20
  const ctx = getComboboxCtx();
21
21
 
22
- let el = $state<HTMLInputElement>();
23
-
24
- $effect(() => {
25
- if (el) {
26
- ctx.inputEl = el;
27
- }
28
- });
22
+ function attachInput(node: HTMLInputElement) {
23
+ ctx.inputEl = node;
24
+ return () => {
25
+ if (ctx.inputEl === node) {
26
+ ctx.inputEl = null;
27
+ }
28
+ };
29
+ }
29
30
 
30
31
  function getOptionItems(contentEl: HTMLElement | null): HTMLElement[] {
31
32
  if (!contentEl) return [];
@@ -91,6 +92,18 @@
91
92
  ctx.close();
92
93
  break;
93
94
  }
95
+ case 'Home': {
96
+ if (!ctx.open || items.length === 0) break;
97
+ e.preventDefault();
98
+ ctx.setActiveIndex(0);
99
+ break;
100
+ }
101
+ case 'End': {
102
+ if (!ctx.open || items.length === 0) break;
103
+ e.preventDefault();
104
+ ctx.setActiveIndex(items.length - 1);
105
+ break;
106
+ }
94
107
  case 'Tab': {
95
108
  if (ctx.open) {
96
109
  if (ctx.activeIndex >= 0 && items[ctx.activeIndex]) {
@@ -109,7 +122,7 @@
109
122
  </script>
110
123
 
111
124
  <input
112
- bind:this={el}
125
+ {@attach attachInput}
113
126
  id={ctx.inputId}
114
127
  type="text"
115
128
  role="combobox"
@@ -1,15 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import Button from '../button/button.svelte';
5
-
6
- interface CountryOption {
7
- code: string;
8
- name: string;
9
- dialCode: string;
10
- flag: string;
11
- region: string;
12
- }
4
+ import { CountrySelect as PrimitiveCountrySelect } from '@dryui/primitives';
13
5
 
14
6
  interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
15
7
  value?: string;
@@ -33,277 +25,149 @@
33
25
  class: className,
34
26
  ...rest
35
27
  }: Props = $props();
28
+ </script>
36
29
 
37
- const allCountries: CountryOption[] = [
38
- {
39
- code: 'AF',
40
- name: 'Afghanistan',
41
- dialCode: '+93',
42
- flag: '\u{1F1E6}\u{1F1EB}',
43
- region: 'Asia'
44
- },
45
- {
46
- code: 'AR',
47
- name: 'Argentina',
48
- dialCode: '+54',
49
- flag: '\u{1F1E6}\u{1F1F7}',
50
- region: 'Americas'
51
- },
52
- {
53
- code: 'AU',
54
- name: 'Australia',
55
- dialCode: '+61',
56
- flag: '\u{1F1E6}\u{1F1FA}',
57
- region: 'Oceania'
58
- },
59
- { code: 'AT', name: 'Austria', dialCode: '+43', flag: '\u{1F1E6}\u{1F1F9}', region: 'Europe' },
60
- { code: 'BE', name: 'Belgium', dialCode: '+32', flag: '\u{1F1E7}\u{1F1EA}', region: 'Europe' },
61
- { code: 'BR', name: 'Brazil', dialCode: '+55', flag: '\u{1F1E7}\u{1F1F7}', region: 'Americas' },
62
- { code: 'CA', name: 'Canada', dialCode: '+1', flag: '\u{1F1E8}\u{1F1E6}', region: 'Americas' },
63
- { code: 'CL', name: 'Chile', dialCode: '+56', flag: '\u{1F1E8}\u{1F1F1}', region: 'Americas' },
64
- { code: 'CN', name: 'China', dialCode: '+86', flag: '\u{1F1E8}\u{1F1F3}', region: 'Asia' },
65
- {
66
- code: 'CO',
67
- name: 'Colombia',
68
- dialCode: '+57',
69
- flag: '\u{1F1E8}\u{1F1F4}',
70
- region: 'Americas'
71
- },
72
- {
73
- code: 'CZ',
74
- name: 'Czech Republic',
75
- dialCode: '+420',
76
- flag: '\u{1F1E8}\u{1F1FF}',
77
- region: 'Europe'
78
- },
79
- { code: 'DK', name: 'Denmark', dialCode: '+45', flag: '\u{1F1E9}\u{1F1F0}', region: 'Europe' },
80
- { code: 'EG', name: 'Egypt', dialCode: '+20', flag: '\u{1F1EA}\u{1F1EC}', region: 'Africa' },
81
- { code: 'FI', name: 'Finland', dialCode: '+358', flag: '\u{1F1EB}\u{1F1EE}', region: 'Europe' },
82
- { code: 'FR', name: 'France', dialCode: '+33', flag: '\u{1F1EB}\u{1F1F7}', region: 'Europe' },
83
- { code: 'DE', name: 'Germany', dialCode: '+49', flag: '\u{1F1E9}\u{1F1EA}', region: 'Europe' },
84
- { code: 'GR', name: 'Greece', dialCode: '+30', flag: '\u{1F1EC}\u{1F1F7}', region: 'Europe' },
85
- { code: 'HK', name: 'Hong Kong', dialCode: '+852', flag: '\u{1F1ED}\u{1F1F0}', region: 'Asia' },
86
- { code: 'IN', name: 'India', dialCode: '+91', flag: '\u{1F1EE}\u{1F1F3}', region: 'Asia' },
87
- { code: 'ID', name: 'Indonesia', dialCode: '+62', flag: '\u{1F1EE}\u{1F1E9}', region: 'Asia' },
88
- { code: 'IE', name: 'Ireland', dialCode: '+353', flag: '\u{1F1EE}\u{1F1EA}', region: 'Europe' },
89
- { code: 'IL', name: 'Israel', dialCode: '+972', flag: '\u{1F1EE}\u{1F1F1}', region: 'Asia' },
90
- { code: 'IT', name: 'Italy', dialCode: '+39', flag: '\u{1F1EE}\u{1F1F9}', region: 'Europe' },
91
- { code: 'JP', name: 'Japan', dialCode: '+81', flag: '\u{1F1EF}\u{1F1F5}', region: 'Asia' },
92
- { code: 'KE', name: 'Kenya', dialCode: '+254', flag: '\u{1F1F0}\u{1F1EA}', region: 'Africa' },
93
- {
94
- code: 'KR',
95
- name: 'South Korea',
96
- dialCode: '+82',
97
- flag: '\u{1F1F0}\u{1F1F7}',
98
- region: 'Asia'
99
- },
100
- { code: 'MY', name: 'Malaysia', dialCode: '+60', flag: '\u{1F1F2}\u{1F1FE}', region: 'Asia' },
101
- { code: 'MX', name: 'Mexico', dialCode: '+52', flag: '\u{1F1F2}\u{1F1FD}', region: 'Americas' },
102
- {
103
- code: 'NL',
104
- name: 'Netherlands',
105
- dialCode: '+31',
106
- flag: '\u{1F1F3}\u{1F1F1}',
107
- region: 'Europe'
108
- },
109
- {
110
- code: 'NZ',
111
- name: 'New Zealand',
112
- dialCode: '+64',
113
- flag: '\u{1F1F3}\u{1F1FF}',
114
- region: 'Oceania'
115
- },
116
- { code: 'NG', name: 'Nigeria', dialCode: '+234', flag: '\u{1F1F3}\u{1F1EC}', region: 'Africa' },
117
- { code: 'NO', name: 'Norway', dialCode: '+47', flag: '\u{1F1F3}\u{1F1F4}', region: 'Europe' },
118
- { code: 'PK', name: 'Pakistan', dialCode: '+92', flag: '\u{1F1F5}\u{1F1F0}', region: 'Asia' },
119
- { code: 'PE', name: 'Peru', dialCode: '+51', flag: '\u{1F1F5}\u{1F1EA}', region: 'Americas' },
120
- {
121
- code: 'PH',
122
- name: 'Philippines',
123
- dialCode: '+63',
124
- flag: '\u{1F1F5}\u{1F1ED}',
125
- region: 'Asia'
126
- },
127
- { code: 'PL', name: 'Poland', dialCode: '+48', flag: '\u{1F1F5}\u{1F1F1}', region: 'Europe' },
128
- {
129
- code: 'PT',
130
- name: 'Portugal',
131
- dialCode: '+351',
132
- flag: '\u{1F1F5}\u{1F1F9}',
133
- region: 'Europe'
134
- },
135
- { code: 'RO', name: 'Romania', dialCode: '+40', flag: '\u{1F1F7}\u{1F1F4}', region: 'Europe' },
136
- { code: 'RU', name: 'Russia', dialCode: '+7', flag: '\u{1F1F7}\u{1F1FA}', region: 'Europe' },
137
- {
138
- code: 'SA',
139
- name: 'Saudi Arabia',
140
- dialCode: '+966',
141
- flag: '\u{1F1F8}\u{1F1E6}',
142
- region: 'Asia'
143
- },
144
- { code: 'SG', name: 'Singapore', dialCode: '+65', flag: '\u{1F1F8}\u{1F1EC}', region: 'Asia' },
145
- {
146
- code: 'ZA',
147
- name: 'South Africa',
148
- dialCode: '+27',
149
- flag: '\u{1F1FF}\u{1F1E6}',
150
- region: 'Africa'
151
- },
152
- { code: 'ES', name: 'Spain', dialCode: '+34', flag: '\u{1F1EA}\u{1F1F8}', region: 'Europe' },
153
- { code: 'SE', name: 'Sweden', dialCode: '+46', flag: '\u{1F1F8}\u{1F1EA}', region: 'Europe' },
154
- {
155
- code: 'CH',
156
- name: 'Switzerland',
157
- dialCode: '+41',
158
- flag: '\u{1F1E8}\u{1F1ED}',
159
- region: 'Europe'
160
- },
161
- { code: 'TW', name: 'Taiwan', dialCode: '+886', flag: '\u{1F1F9}\u{1F1FC}', region: 'Asia' },
162
- { code: 'TH', name: 'Thailand', dialCode: '+66', flag: '\u{1F1F9}\u{1F1ED}', region: 'Asia' },
163
- { code: 'TR', name: 'Turkey', dialCode: '+90', flag: '\u{1F1F9}\u{1F1F7}', region: 'Europe' },
164
- {
165
- code: 'AE',
166
- name: 'United Arab Emirates',
167
- dialCode: '+971',
168
- flag: '\u{1F1E6}\u{1F1EA}',
169
- region: 'Asia'
170
- },
171
- {
172
- code: 'GB',
173
- name: 'United Kingdom',
174
- dialCode: '+44',
175
- flag: '\u{1F1EC}\u{1F1E7}',
176
- region: 'Europe'
177
- },
178
- {
179
- code: 'US',
180
- name: 'United States',
181
- dialCode: '+1',
182
- flag: '\u{1F1FA}\u{1F1F8}',
183
- region: 'Americas'
184
- },
185
- { code: 'VN', name: 'Vietnam', dialCode: '+84', flag: '\u{1F1FB}\u{1F1F3}', region: 'Asia' }
186
- ];
30
+ <span data-country-select-wrapper class={className}>
31
+ <PrimitiveCountrySelect
32
+ bind:value
33
+ {regions}
34
+ {showDialCode}
35
+ {disabled}
36
+ {placeholder}
37
+ name={formName}
38
+ {onchange}
39
+ {...rest}
40
+ />
41
+ </span>
187
42
 
188
- const filteredCountries = $derived(
189
- regions ? allCountries.filter((c) => regions.includes(c.region)) : allCountries
190
- );
43
+ <style>
44
+ [data-country-select-wrapper] {
45
+ display: contents;
46
+ }
191
47
 
192
- let searchQuery = $state('');
193
- let open = $state(false);
48
+ [data-country-select-wrapper] [data-part='country-select'] {
49
+ display: inline-grid;
50
+ grid-template-columns: minmax(14rem, max-content);
51
+ }
194
52
 
195
- const searchResults = $derived(
196
- searchQuery
197
- ? filteredCountries.filter(
198
- (c) =>
199
- c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
200
- c.code.toLowerCase().includes(searchQuery.toLowerCase()) ||
201
- c.dialCode.includes(searchQuery)
202
- )
203
- : filteredCountries
204
- );
53
+ [data-country-select-wrapper] [data-part='control'] {
54
+ display: grid;
55
+ grid-template-columns: auto minmax(0, 1fr) auto;
56
+ align-items: center;
57
+ gap: var(--dry-space-2, 0.5rem);
58
+ min-height: 2.75rem;
59
+ padding-inline: var(--dry-space-3, 0.75rem);
60
+ border: 1px solid var(--dry-color-stroke-weak);
61
+ border-radius: var(--dry-radius-md);
62
+ background: var(--dry-color-bg-raised);
63
+ box-shadow: var(--dry-shadow-xs);
64
+ transition:
65
+ border-color 140ms ease,
66
+ box-shadow 140ms ease,
67
+ background-color 140ms ease;
68
+ }
205
69
 
206
- const selectedCountry = $derived(filteredCountries.find((c) => c.code === value));
70
+ [data-country-select-wrapper] [data-part='control'][data-state='open'] {
71
+ border-color: color-mix(
72
+ in srgb,
73
+ var(--dry-color-brand-primary) 52%,
74
+ var(--dry-color-stroke-weak)
75
+ );
76
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dry-color-brand-primary) 18%, transparent);
77
+ }
207
78
 
208
- function selectCountry(code: string) {
209
- value = code;
210
- open = false;
211
- searchQuery = '';
212
- onchange?.(code);
79
+ [data-country-select-wrapper] [data-part='control'][data-disabled] {
80
+ opacity: 0.6;
213
81
  }
214
- </script>
215
82
 
216
- <span data-country-select-wrapper class={className}>
217
- <div data-part="country-select" {...rest}>
218
- <Button
219
- variant="outline"
220
- type="button"
221
- data-part="trigger"
222
- {disabled}
223
- onclick={() => {
224
- if (!disabled) open = !open;
225
- }}
226
- aria-expanded={open}
227
- aria-haspopup="listbox"
228
- >
229
- {#if selectedCountry}
230
- <span data-part="flag">{selectedCountry.flag}</span>
231
- <span data-part="country-name">{selectedCountry.name}</span>
232
- {#if showDialCode}
233
- <span data-part="dial-code">{selectedCountry.dialCode}</span>
234
- {/if}
235
- {:else}
236
- <span data-part="placeholder">{placeholder}</span>
237
- {/if}
238
- </Button>
83
+ [data-country-select-wrapper] [data-part='flag'] {
84
+ font-size: 1rem;
85
+ line-height: 1;
86
+ }
239
87
 
240
- {#if open}
241
- <div data-part="dropdown" role="listbox" aria-label="Countries">
242
- <input
243
- type="text"
244
- data-part="search"
245
- placeholder="Search countries..."
246
- bind:value={searchQuery}
247
- aria-label="Search countries"
248
- />
249
- {#each searchResults as country (country.code)}
250
- <Button
251
- variant="trigger"
252
- type="button"
253
- role="option"
254
- data-part="option"
255
- aria-selected={country.code === value}
256
- data-selected={country.code === value ? '' : undefined}
257
- onclick={() => selectCountry(country.code)}
258
- >
259
- <span data-part="flag">{country.flag}</span>
260
- <span data-part="country-name">{country.name}</span>
261
- {#if showDialCode}
262
- <span data-part="dial-code">{country.dialCode}</span>
263
- {/if}
264
- </Button>
265
- {/each}
266
- </div>
267
- {/if}
88
+ [data-country-select-wrapper] [data-part='input'] {
89
+ border: none;
90
+ outline: none;
91
+ padding: 0;
92
+ background: transparent;
93
+ color: var(--dry-color-text-strong);
94
+ font-family: var(--dry-font-sans);
95
+ font-size: var(--dry-type-body-size, var(--dry-text-md-size));
96
+ line-height: var(--dry-type-body-leading, var(--dry-text-md-leading));
97
+ }
268
98
 
269
- {#if formName}
270
- <input type="hidden" name={formName} {value} />
271
- {/if}
272
- </div>
273
- </span>
99
+ [data-country-select-wrapper] [data-part='input']::placeholder {
100
+ color: var(--dry-color-text-weak);
101
+ }
274
102
 
275
- <style>
276
- [data-country-select-wrapper] {
277
- position: relative;
278
- display: inline-grid;
279
- grid-template-columns: minmax(12rem, max-content);
103
+ [data-country-select-wrapper] [data-part='dial-code'] {
104
+ color: var(--dry-color-text-weak);
105
+ font-size: var(--dry-type-small-size, var(--dry-text-sm-size));
280
106
  }
281
107
 
282
108
  [data-country-select-wrapper] [data-part='dropdown'] {
283
- position: absolute;
284
- top: 100%;
285
- left: 0;
286
- right: 0;
287
- z-index: 50;
288
109
  display: grid;
289
- margin-top: var(--dry-space-1, 0.25rem);
290
- background: var(--dry-color-bg-raised);
110
+ grid-template-columns: minmax(max(14rem, anchor-size(inline)), max-content);
111
+ gap: var(--dry-space-1, 0.25rem);
112
+ max-height: 18rem;
113
+ padding: var(--dry-space-1, 0.25rem);
291
114
  border: 1px solid var(--dry-color-stroke-weak);
292
- border-radius: var(--dry-radius-md);
115
+ border-radius: calc(var(--dry-radius-md) + 0.125rem);
116
+ background: var(--dry-color-bg-raised);
293
117
  box-shadow: var(--dry-shadow-lg);
294
- max-height: 16rem;
295
- overflow-y: auto;
118
+ inset: unset;
119
+ margin: 0;
296
120
  }
297
121
 
298
- [data-country-select-wrapper] [data-part='search'] {
299
- padding: var(--dry-space-2) var(--dry-space-3);
300
- font-family: var(--dry-font-sans);
301
- font-size: var(--dry-type-small-size);
122
+ [data-country-select-wrapper] [data-part='option'] {
123
+ display: grid;
124
+ grid-template-columns: auto minmax(0, 1fr) auto;
125
+ align-items: center;
126
+ gap: var(--dry-space-2, 0.5rem);
127
+ justify-self: stretch;
128
+ padding: var(--dry-space-2, 0.5rem) var(--dry-space-3, 0.75rem);
129
+ border: none;
130
+ border-radius: calc(var(--dry-radius-sm) + 0.0625rem);
302
131
  background: transparent;
303
132
  color: var(--dry-color-text-strong);
304
- border: none;
305
- border-bottom: 1px solid var(--dry-color-stroke-weak);
306
- outline: none;
307
- box-sizing: border-box;
133
+ font-family: var(--dry-font-sans);
134
+ font-size: var(--dry-type-small-size, var(--dry-text-sm-size));
135
+ line-height: var(--dry-type-small-leading, var(--dry-text-sm-leading));
136
+ text-align: left;
137
+ cursor: pointer;
138
+ }
139
+
140
+ [data-country-select-wrapper] [data-part='option']::before {
141
+ content: attr(data-flag);
142
+ font-size: 1rem;
143
+ line-height: 1;
144
+ }
145
+
146
+ [data-country-select-wrapper] [data-part='option'][data-dial-code]::after {
147
+ content: attr(data-dial-code);
148
+ color: var(--dry-color-text-weak);
149
+ font-size: var(--dry-type-small-size, var(--dry-text-sm-size));
150
+ }
151
+
152
+ [data-country-select-wrapper] [data-part='option'][data-highlighted],
153
+ [data-country-select-wrapper] [data-part='option']:hover {
154
+ background: color-mix(in srgb, var(--dry-color-brand-primary) 12%, var(--dry-color-bg-raised));
155
+ }
156
+
157
+ [data-country-select-wrapper] [data-part='option'][data-state='selected'] {
158
+ background: color-mix(in srgb, var(--dry-color-brand-primary) 18%, var(--dry-color-bg-raised));
159
+ color: var(--dry-color-text-strong);
160
+ }
161
+
162
+ [data-country-select-wrapper] [data-part='country-name'] {
163
+ overflow: hidden;
164
+ text-overflow: ellipsis;
165
+ white-space: nowrap;
166
+ }
167
+
168
+ [data-country-select-wrapper] [data-part='empty'] {
169
+ padding: var(--dry-space-2, 0.5rem) var(--dry-space-3, 0.75rem);
170
+ color: var(--dry-color-text-weak);
171
+ font-size: var(--dry-type-small-size, var(--dry-text-sm-size));
308
172
  }
309
173
  </style>
@@ -1,23 +1,12 @@
1
1
  <script lang="ts">
2
+ import { fromAction } from 'svelte/attachments';
2
3
  import type { Snippet } from 'svelte';
3
4
  import type { HTMLAttributes } from 'svelte/elements';
5
+ import { createAnchoredPopover, type Placement } from '@dryui/primitives';
4
6
  import { getDatePickerCtx } from './context.svelte.js';
5
- import { useAnchorStyles } from '../utils/use-anchor-styles.svelte.js';
6
7
 
7
8
  interface Props extends HTMLAttributes<HTMLDivElement> {
8
- placement?:
9
- | 'top'
10
- | 'top-start'
11
- | 'top-end'
12
- | 'bottom'
13
- | 'bottom-start'
14
- | 'bottom-end'
15
- | 'left'
16
- | 'left-start'
17
- | 'left-end'
18
- | 'right'
19
- | 'right-start'
20
- | 'right-end';
9
+ placement?: Placement;
21
10
  offset?: number;
22
11
  children: Snippet;
23
12
  }
@@ -33,26 +22,30 @@
33
22
 
34
23
  const ctx = getDatePickerCtx();
35
24
 
36
- let el = $state<HTMLDivElement>();
25
+ let el = $state<HTMLDivElement | null>(null);
37
26
 
38
- const anchor = useAnchorStyles({
27
+ function attachContent(node: HTMLDivElement) {
28
+ el = node;
29
+
30
+ return () => {
31
+ if (el === node) {
32
+ el = null;
33
+ }
34
+ };
35
+ }
36
+
37
+ const popover = createAnchoredPopover({
39
38
  triggerEl: () => ctx.triggerEl,
40
39
  contentEl: () => el ?? null,
40
+ open: () => ctx.open,
41
41
  placement: () => placement,
42
42
  offset: () => offset
43
43
  });
44
-
45
- $effect(() => {
46
- if (ctx.open && el && !el.matches(':popover-open')) {
47
- el.showPopover();
48
- } else if (!ctx.open && el?.matches(':popover-open')) {
49
- el.hidePopover();
50
- }
51
- });
52
44
  </script>
53
45
 
54
46
  <div
55
- bind:this={el}
47
+ {@attach attachContent}
48
+ {@attach fromAction(popover.applyPosition, () => style)}
56
49
  popover="auto"
57
50
  role="dialog"
58
51
  id={ctx.contentId}
@@ -60,7 +53,6 @@
60
53
  data-state={ctx.open ? 'open' : 'closed'}
61
54
  data-dp-content
62
55
  class={className}
63
- use:anchor.applyPosition={style}
64
56
  ontoggle={(e) => {
65
57
  const newState = (e as ToggleEvent).newState === 'open';
66
58
  if (newState && !ctx.open) {
@@ -1,7 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type Placement } from '@dryui/primitives';
3
4
  interface Props extends HTMLAttributes<HTMLDivElement> {
4
- placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end';
5
+ placement?: Placement;
5
6
  offset?: number;
6
7
  children: Snippet;
7
8
  }
@@ -34,6 +34,7 @@
34
34
  // View state: which month/year is the calendar showing
35
35
  let viewMonth = $state(value ? value.getMonth() : new Date().getMonth());
36
36
  let viewYear = $state(value ? value.getFullYear() : new Date().getFullYear());
37
+ let triggerEl = $state<HTMLElement | null>(null);
37
38
 
38
39
  // The day that has keyboard focus within the calendar grid
39
40
  let focusedDate = $state<Date>(value ?? new Date());
@@ -81,7 +82,12 @@
81
82
  },
82
83
  triggerId,
83
84
  contentId,
84
- triggerEl: null,
85
+ get triggerEl() {
86
+ return triggerEl;
87
+ },
88
+ set triggerEl(element: HTMLElement | null) {
89
+ triggerEl = element;
90
+ },
85
91
  show() {
86
92
  if (!disabled) open = true;
87
93
  },
@@ -1,11 +1,12 @@
1
1
  <script lang="ts">
2
+ import { fromAction } from 'svelte/attachments';
2
3
  import type { Snippet } from 'svelte';
3
4
  import type { HTMLAttributes } from 'svelte/elements';
5
+ import { createAnchoredPopover, type Placement } from '@dryui/primitives';
4
6
  import { getDateRangePickerCtx } from './context.svelte.js';
5
- import { useAnchorStyles } from '../utils/use-anchor-styles.svelte.js';
6
7
 
7
8
  interface Props extends HTMLAttributes<HTMLDivElement> {
8
- placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end';
9
+ placement?: Placement;
9
10
  offset?: number;
10
11
  children: Snippet;
11
12
  }
@@ -21,26 +22,30 @@
21
22
 
22
23
  const ctx = getDateRangePickerCtx();
23
24
 
24
- let el = $state<HTMLDivElement>();
25
+ let el = $state<HTMLDivElement | null>(null);
25
26
 
26
- const anchor = useAnchorStyles({
27
+ function attachContent(node: HTMLDivElement) {
28
+ el = node;
29
+
30
+ return () => {
31
+ if (el === node) {
32
+ el = null;
33
+ }
34
+ };
35
+ }
36
+
37
+ const popover = createAnchoredPopover({
27
38
  triggerEl: () => ctx.triggerEl,
28
39
  contentEl: () => el ?? null,
40
+ open: () => ctx.open,
29
41
  placement: () => placement,
30
42
  offset: () => offset
31
43
  });
32
-
33
- $effect(() => {
34
- if (ctx.open && el && !el.matches(':popover-open')) {
35
- el.showPopover();
36
- } else if (!ctx.open && el?.matches(':popover-open')) {
37
- el.hidePopover();
38
- }
39
- });
40
44
  </script>
41
45
 
42
46
  <div
43
- bind:this={el}
47
+ {@attach attachContent}
48
+ {@attach fromAction(popover.applyPosition, () => style)}
44
49
  popover="auto"
45
50
  role="dialog"
46
51
  id={ctx.contentId}
@@ -48,7 +53,6 @@
48
53
  data-state={ctx.open ? 'open' : 'closed'}
49
54
  data-drp-content
50
55
  class={className}
51
- use:anchor.applyPosition={style}
52
56
  ontoggle={(e) => {
53
57
  const newState = (e as ToggleEvent).newState === 'open';
54
58
  if (newState && !ctx.open) {
@@ -1,7 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type Placement } from '@dryui/primitives';
3
4
  interface Props extends HTMLAttributes<HTMLDivElement> {
4
- placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end';
5
+ placement?: Placement;
5
6
  offset?: number;
6
7
  children: Snippet;
7
8
  }
@@ -34,6 +34,7 @@
34
34
  // View state: which month/year is the calendar showing
35
35
  let viewMonth = $state(startDate ? startDate.getMonth() : new Date().getMonth());
36
36
  let viewYear = $state(startDate ? startDate.getFullYear() : new Date().getFullYear());
37
+ let triggerEl = $state<HTMLElement | null>(null);
37
38
 
38
39
  // The day that has keyboard focus within the calendar grid
39
40
  let focusedDate = $state<Date>(startDate ?? new Date());
@@ -86,7 +87,12 @@
86
87
  },
87
88
  triggerId,
88
89
  contentId,
89
- triggerEl: null,
90
+ get triggerEl() {
91
+ return triggerEl;
92
+ },
93
+ set triggerEl(element: HTMLElement | null) {
94
+ triggerEl = element;
95
+ },
90
96
  show() {
91
97
  if (!disabled) {
92
98
  selecting = 'start';