@x33025/sveltely 0.1.1 → 0.1.2

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 (101) hide show
  1. package/dist/components/Library/Button/Button.demo.svelte +5 -3
  2. package/dist/components/Library/Button/Button.demo.svelte.d.ts +1 -0
  3. package/dist/components/Library/Calendar/Calendar.demo.svelte +2 -14
  4. package/dist/components/Library/Calendar/Calendar.svelte +54 -50
  5. package/dist/components/Library/Divider/Divider.svelte +10 -0
  6. package/dist/components/Library/Divider/Divider.svelte.d.ts +26 -0
  7. package/dist/components/Library/Divider/index.d.ts +1 -0
  8. package/dist/components/Library/Divider/index.js +1 -0
  9. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +10 -3
  10. package/dist/components/Library/Dropdown/Dropdown.svelte +39 -5
  11. package/dist/components/Library/Dropdown/index.d.ts +1 -1
  12. package/dist/components/Library/Dropdown/types.d.ts +4 -1
  13. package/dist/components/Library/Floating/Floating.svelte +35 -1
  14. package/dist/components/Library/ForEach/ForEach.svelte +14 -0
  15. package/dist/components/Library/ForEach/ForEach.svelte.d.ts +28 -0
  16. package/dist/components/Library/ForEach/index.d.ts +1 -0
  17. package/dist/components/Library/ForEach/index.js +1 -0
  18. package/dist/components/Library/Grid/Grid.svelte +74 -0
  19. package/dist/components/Library/Grid/Grid.svelte.d.ts +13 -0
  20. package/dist/components/Library/Grid/index.d.ts +1 -0
  21. package/dist/components/Library/Grid/index.js +1 -0
  22. package/dist/components/Library/GridItem/GridItem.svelte +65 -0
  23. package/dist/components/Library/GridItem/GridItem.svelte.d.ts +14 -0
  24. package/dist/components/Library/GridItem/index.d.ts +1 -0
  25. package/dist/components/Library/GridItem/index.js +1 -0
  26. package/dist/components/Library/HStack/HStack.svelte +45 -0
  27. package/dist/components/Library/HStack/HStack.svelte.d.ts +9 -0
  28. package/dist/components/Library/HStack/index.d.ts +1 -0
  29. package/dist/components/Library/HStack/index.js +1 -0
  30. package/dist/components/Library/Image/Image.demo.svelte +18 -0
  31. package/dist/components/Library/Image/Image.demo.svelte.d.ts +23 -0
  32. package/dist/components/Library/Image/Image.svelte +57 -0
  33. package/dist/components/Library/Image/Image.svelte.d.ts +17 -0
  34. package/dist/components/Library/Image/ImagePlaceholder.svelte +202 -0
  35. package/dist/components/Library/Image/ImagePlaceholder.svelte.d.ts +7 -0
  36. package/dist/components/Library/Image/index.d.ts +1 -0
  37. package/dist/components/Library/Image/index.js +1 -0
  38. package/dist/components/Library/ImageMask/BrushPreview.svelte +119 -0
  39. package/dist/components/Library/ImageMask/BrushPreview.svelte.d.ts +11 -0
  40. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +117 -0
  41. package/dist/components/Library/ImageMask/ImageMask.demo.svelte.d.ts +10 -0
  42. package/dist/components/Library/ImageMask/ImageMask.svelte +46 -0
  43. package/dist/components/Library/ImageMask/ImageMask.svelte.d.ts +20 -0
  44. package/dist/components/Library/ImageMask/MaskLayer.svelte +341 -0
  45. package/dist/components/Library/ImageMask/MaskLayer.svelte.d.ts +12 -0
  46. package/dist/components/Library/ImageMask/contour.d.ts +11 -0
  47. package/dist/components/Library/ImageMask/contour.js +152 -0
  48. package/dist/components/Library/ImageMask/index.d.ts +2 -0
  49. package/dist/components/Library/ImageMask/index.js +1 -0
  50. package/dist/components/Library/ImageMask/marchingAnts.d.ts +8 -0
  51. package/dist/components/Library/ImageMask/marchingAnts.js +29 -0
  52. package/dist/components/Library/ImageMask/maskSurface.d.ts +5 -0
  53. package/dist/components/Library/ImageMask/maskSurface.js +94 -0
  54. package/dist/components/Library/ImageMask/types.d.ts +23 -0
  55. package/dist/components/Library/ImageMask/types.js +1 -0
  56. package/dist/components/Library/Label/Label.demo.svelte +28 -0
  57. package/dist/components/Library/Label/Label.demo.svelte.d.ts +9 -0
  58. package/dist/components/Library/Label/Label.svelte +177 -0
  59. package/dist/components/Library/Label/Label.svelte.d.ts +18 -0
  60. package/dist/components/Library/Label/index.d.ts +1 -0
  61. package/dist/components/Library/Label/index.js +1 -0
  62. package/dist/components/Library/NumberField/NumberField.demo.svelte +21 -0
  63. package/dist/components/Library/NumberField/NumberField.demo.svelte.d.ts +8 -0
  64. package/dist/components/Library/NumberField/NumberField.svelte +194 -0
  65. package/dist/components/Library/NumberField/NumberField.svelte.d.ts +21 -0
  66. package/dist/components/Library/NumberField/index.d.ts +1 -0
  67. package/dist/components/Library/NumberField/index.js +1 -0
  68. package/dist/components/Library/ScrollView/ScrollView.svelte +25 -9
  69. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +4 -4
  70. package/dist/components/Library/Spacer/Spacer.svelte +7 -0
  71. package/dist/components/Library/Spacer/Spacer.svelte.d.ts +26 -0
  72. package/dist/components/Library/Spacer/index.d.ts +1 -0
  73. package/dist/components/Library/Spacer/index.js +1 -0
  74. package/dist/components/Library/TextField/TextField.demo.svelte +14 -0
  75. package/dist/components/Library/TextField/TextField.demo.svelte.d.ts +8 -0
  76. package/dist/components/Library/TextField/TextField.svelte +149 -0
  77. package/dist/components/Library/TextField/TextField.svelte.d.ts +19 -0
  78. package/dist/components/Library/TextField/index.d.ts +1 -0
  79. package/dist/components/Library/TextField/index.js +1 -0
  80. package/dist/components/Library/VStack/VStack.svelte +45 -0
  81. package/dist/components/Library/VStack/VStack.svelte.d.ts +9 -0
  82. package/dist/components/Library/VStack/index.d.ts +1 -0
  83. package/dist/components/Library/VStack/index.js +1 -0
  84. package/dist/components/Local/ComponentGrid.svelte +15 -31
  85. package/dist/components/Local/HeroCard.svelte +26 -36
  86. package/dist/components/Local/HeroCard.svelte.d.ts +0 -2
  87. package/dist/index.d.ts +23 -0
  88. package/dist/index.js +17 -0
  89. package/dist/style/index.css +28 -17
  90. package/dist/style/label.d.ts +6 -0
  91. package/dist/style/label.js +4 -0
  92. package/dist/style/layout.d.ts +57 -0
  93. package/dist/style/layout.js +128 -0
  94. package/dist/style/media.d.ts +12 -0
  95. package/dist/style/media.js +8 -0
  96. package/dist/style/scroll.d.ts +7 -0
  97. package/dist/style/scroll.js +5 -0
  98. package/dist/style/text-editor.d.ts +34 -0
  99. package/dist/style/text-editor.js +29 -0
  100. package/dist/style.css +58 -35
  101. package/package.json +1 -1
@@ -1,17 +1,19 @@
1
1
  <script module lang="ts">
2
2
  export const demo = {
3
3
  name: 'Button',
4
- description: 'Token-aware button primitive with optional icon support.'
4
+ description: 'Token-aware button primitive with optional icon support.',
5
+ columnSpan: 2
5
6
  };
6
7
  </script>
7
8
 
8
9
  <script lang="ts">
9
10
  import { SaveIcon } from '@lucide/svelte';
10
11
  import Button from './Button.svelte';
12
+ import HStack from '../HStack';
11
13
  </script>
12
14
 
13
- <div class="hstack items-center gap-3">
15
+ <HStack align="center" gap={0.75}>
14
16
  <Button label="Default" />
15
17
  <Button label="Solid" variant="solid" />
16
18
  <Button icon={SaveIcon} label="With icon" />
17
- </div>
19
+ </HStack>
@@ -1,6 +1,7 @@
1
1
  export declare const demo: {
2
2
  name: string;
3
3
  description: string;
4
+ columnSpan: number;
4
5
  };
5
6
  import Button from './Button.svelte';
6
7
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
@@ -3,7 +3,7 @@
3
3
  name: 'Calendar',
4
4
  description: 'Compact month calendar with token-driven spacing and radius.',
5
5
  columnSpan: 2,
6
- rowSpan: 2
6
+ rowSpan: 3
7
7
  };
8
8
  </script>
9
9
 
@@ -15,16 +15,4 @@
15
15
  let weekStart = $state<'monday' | 'sunday'>('monday');
16
16
  </script>
17
17
 
18
- <div class="vstack gap-2">
19
- <div class="hstack gap-2">
20
- <button type="button" onclick={() => (weekStart = 'monday')}>Monday start</button>
21
- <button type="button" onclick={() => (weekStart = 'sunday')}>Sunday start</button>
22
- </div>
23
- <Calendar bind:value bind:month {weekStart} />
24
- <p class="text-xs text-zinc-500">
25
- Selected: {value ? value.toLocaleDateString() : 'none'} | Month: {month.toLocaleDateString(undefined, {
26
- month: 'long',
27
- year: 'numeric'
28
- })} | Week starts: {weekStart}
29
- </p>
30
- </div>
18
+ <Calendar bind:value bind:month {weekStart} />
@@ -5,8 +5,7 @@
5
5
  const startOfDay = (value: Date) =>
6
6
  new Date(value.getFullYear(), value.getMonth(), value.getDate());
7
7
 
8
- const startOfMonth = (value: Date) =>
9
- new Date(value.getFullYear(), value.getMonth(), 1);
8
+ const startOfMonth = (value: Date) => new Date(value.getFullYear(), value.getMonth(), 1);
10
9
 
11
10
  type CalendarCell = {
12
11
  key: string;
@@ -23,7 +22,8 @@
23
22
  month?: Date;
24
23
  weekdayLabels?: string[];
25
24
  weekStart?: 'monday' | 'sunday';
26
- } & StyleProps & Record<string, unknown>;
25
+ } & StyleProps &
26
+ Record<string, unknown>;
27
27
 
28
28
  const defaultWeekdayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
29
29
  const today = new Date();
@@ -42,14 +42,10 @@
42
42
  const rootStyle = $derived.by(() => surfaceStyle(styleProps, 'calendar'));
43
43
 
44
44
  const orderedWeekdayLabels = $derived(
45
- weekStart === 'sunday'
46
- ? [weekdayLabels[6], ...weekdayLabels.slice(0, 6)]
47
- : weekdayLabels
45
+ weekStart === 'sunday' ? [weekdayLabels[6], ...weekdayLabels.slice(0, 6)] : weekdayLabels
48
46
  );
49
47
 
50
- const weekendColumnIndexes = $derived(
51
- weekStart === 'sunday' ? [0, 6] : [5, 6]
52
- );
48
+ const weekendColumnIndexes = $derived(weekStart === 'sunday' ? [0, 6] : [5, 6]);
53
49
 
54
50
  function shiftMonth(offset: number) {
55
51
  month = new Date(month.getFullYear(), month.getMonth() + offset, 1);
@@ -59,6 +55,17 @@
59
55
  value = startOfDay(nextValue);
60
56
  }
61
57
 
58
+ function activateCell(cell: CalendarCell) {
59
+ if (!cell.value) return;
60
+
61
+ if (cell.isCurrentMonth) {
62
+ selectDate(cell.value);
63
+ return;
64
+ }
65
+
66
+ month = startOfMonth(cell.value);
67
+ }
68
+
62
69
  const displayYear = $derived(month.getFullYear());
63
70
  const displayMonth = $derived(month.getMonth());
64
71
  const todayYear = today.getFullYear();
@@ -74,37 +81,28 @@
74
81
 
75
82
  const cells = $derived.by(() => {
76
83
  const firstDay = new Date(displayYear, displayMonth, 1);
77
- const daysInMonth = new Date(displayYear, displayMonth + 1, 0).getDate();
78
84
  const leadingEmptyDays =
79
85
  weekStart === 'sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
80
86
 
81
- return Array.from({ length: leadingEmptyDays + daysInMonth }, (_, index): CalendarCell => {
87
+ return Array.from({ length: 42 }, (_, index): CalendarCell => {
82
88
  const dayNumber = index - leadingEmptyDays + 1;
83
- if (dayNumber < 1) {
84
- return {
85
- key: `empty-${index}`,
86
- label: '',
87
- isCurrentMonth: false,
88
- isToday: false,
89
- isWeekend: false,
90
- isSelected: false
91
- };
92
- }
93
-
94
89
  const cellDate = new Date(displayYear, displayMonth, dayNumber);
95
90
  const dayOfWeek = cellDate.getDay();
91
+ const isCurrentMonth = cellDate.getMonth() === displayMonth;
96
92
 
97
93
  return {
98
- key: `day-${dayNumber}`,
99
- label: String(dayNumber),
100
- isCurrentMonth: true,
94
+ key: `day-${cellDate.getFullYear()}-${cellDate.getMonth()}-${cellDate.getDate()}`,
95
+ label: String(cellDate.getDate()),
96
+ isCurrentMonth,
101
97
  value: cellDate,
102
98
  isToday:
99
+ isCurrentMonth &&
103
100
  displayYear === todayYear &&
104
101
  displayMonth === todayMonth &&
105
- dayNumber === todayDate,
102
+ cellDate.getDate() === todayDate,
106
103
  isWeekend: dayOfWeek === 0 || dayOfWeek === 6,
107
104
  isSelected:
105
+ isCurrentMonth &&
108
106
  value !== null &&
109
107
  cellDate.getFullYear() === value.getFullYear() &&
110
108
  cellDate.getMonth() === value.getMonth() &&
@@ -116,16 +114,26 @@
116
114
 
117
115
  <div class="calendar vstack" style={rootStyle} {...props}>
118
116
  <div class="calendar-grid">
119
- <button class="calendar-nav-button" type="button" aria-label="Previous month" onclick={() => shiftMonth(-1)}>
117
+ <button
118
+ class="calendar-nav-button"
119
+ type="button"
120
+ aria-label="Previous month"
121
+ onclick={() => shiftMonth(-1)}
122
+ >
120
123
  <ChevronLeftIcon class="calendar-nav-icon" strokeWidth={2} />
121
124
  </button>
122
125
  <div class="calendar-title-cell">
123
126
  <h2 class="calendar-title">{monthLabel}</h2>
124
127
  </div>
125
- <button class="calendar-nav-button" type="button" aria-label="Next month" onclick={() => shiftMonth(1)}>
128
+ <button
129
+ class="calendar-nav-button"
130
+ type="button"
131
+ aria-label="Next month"
132
+ onclick={() => shiftMonth(1)}
133
+ >
126
134
  <ChevronRightIcon class="calendar-nav-icon" strokeWidth={2} />
127
135
  </button>
128
- {#each orderedWeekdayLabels as weekday, index}
136
+ {#each orderedWeekdayLabels as weekday, index (weekday)}
129
137
  <div
130
138
  class="calendar-weekday"
131
139
  class:calendar-weekday-weekend={weekendColumnIndexes.includes(index)}
@@ -135,23 +143,18 @@
135
143
  {/each}
136
144
 
137
145
  {#each cells as cell (cell.key)}
138
- {#if cell.isCurrentMonth && cell.value}
139
- <button
140
- type="button"
141
- class="calendar-day"
142
- class:calendar-day-today={cell.isToday}
143
- class:calendar-day-selected={cell.isSelected}
144
- class:calendar-day-weekend={cell.isWeekend}
145
- aria-pressed={cell.isSelected}
146
- onclick={() => selectDate(cell.value!)}
147
- >
148
- {cell.label}
149
- </button>
150
- {:else}
151
- <div class="calendar-day calendar-day-empty">
152
- {cell.label}
153
- </div>
154
- {/if}
146
+ <button
147
+ type="button"
148
+ class="calendar-day"
149
+ class:calendar-day-outside={!cell.isCurrentMonth}
150
+ class:calendar-day-today={cell.isToday}
151
+ class:calendar-day-selected={cell.isSelected}
152
+ class:calendar-day-weekend={cell.isWeekend}
153
+ aria-pressed={cell.isSelected}
154
+ onclick={() => activateCell(cell)}
155
+ >
156
+ {cell.label}
157
+ </button>
155
158
  {/each}
156
159
  </div>
157
160
  </div>
@@ -165,7 +168,9 @@
165
168
  --calendar-weekend-header-color: var(--color-zinc-400);
166
169
  --calendar-weekend-selected-color: white;
167
170
  --calendar-cell-width: calc(var(--calendar-cell-core-size) + (var(--sveltely-padding-x) * 2.2));
168
- --calendar-cell-height: calc(var(--calendar-cell-core-size) + (var(--sveltely-padding-y) * 2.2));
171
+ --calendar-cell-height: calc(
172
+ var(--calendar-cell-core-size) + (var(--sveltely-padding-y) * 2.2)
173
+ );
169
174
  --calendar-title-size: calc(var(--calendar-font-size) * 1.03);
170
175
  --calendar-weekday-size: calc(var(--calendar-font-size) * 0.75);
171
176
  font-size: var(--calendar-font-size);
@@ -283,11 +288,10 @@
283
288
  background: var(--sveltely-hover-color);
284
289
  }
285
290
 
286
- .calendar-day-empty {
291
+ .calendar-day-outside {
287
292
  border-color: transparent;
288
293
  background: transparent;
289
- color: transparent;
290
- cursor: default;
294
+ color: var(--color-zinc-400);
291
295
  }
292
296
 
293
297
  .calendar-day-today {
@@ -0,0 +1,10 @@
1
+ <div class="divider" role="separator"></div>
2
+
3
+ <style>
4
+ .divider {
5
+ width: var(--divider-width, 100%);
6
+ height: var(--divider-height, 1px);
7
+ align-self: var(--divider-align-self, auto);
8
+ background: var(--color-gray-200);
9
+ }
10
+ </style>
@@ -0,0 +1,26 @@
1
+ export default Divider;
2
+ type Divider = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const Divider: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
@@ -0,0 +1 @@
1
+ export { default } from './Divider.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './Divider.svelte';
@@ -7,19 +7,22 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import Dropdown from './Dropdown.svelte';
10
+ import type { DropdownEntry } from './types';
10
11
 
11
- const allItems = [
12
+ const allItems: DropdownEntry<string>[] = [
12
13
  {
13
14
  type: 'group' as const,
14
15
  label: 'Publishing',
15
16
  items: [
16
17
  { label: 'Draft', value: 'draft' },
17
18
  { label: 'Scheduled', value: 'scheduled' },
19
+ { type: 'divider' },
18
20
  {
19
21
  type: 'submenu' as const,
20
22
  label: 'Advanced',
21
23
  items: [
22
24
  { label: 'Review queue', value: 'review' },
25
+ { type: 'divider' },
23
26
  {
24
27
  type: 'action' as const,
25
28
  label: 'Open workflow',
@@ -48,11 +51,15 @@
48
51
  }
49
52
  ];
50
53
 
51
- const filterEntries = (entries: typeof allItems, query: string): typeof allItems => {
54
+ const filterEntries = (entries: DropdownEntry<string>[], query: string): DropdownEntry<string>[] => {
52
55
  const normalizedQuery = query.trim().toLowerCase();
53
56
  if (!normalizedQuery) return allItems;
54
57
 
55
- return entries.flatMap((entry) => {
58
+ return entries.flatMap<DropdownEntry<string>>((entry) => {
59
+ if (entry.type === 'divider') {
60
+ return [];
61
+ }
62
+
56
63
  if (entry.type === 'group') {
57
64
  const nextItems = filterEntries(entry.items, normalizedQuery);
58
65
  return nextItems.length > 0 ? [{ ...entry, items: nextItems }] : [];
@@ -1,11 +1,13 @@
1
1
  <script lang="ts" generics="T extends string | number = string">
2
2
  import { CheckIcon, ChevronDownIcon, ChevronRightIcon } from '@lucide/svelte';
3
+ import Divider from '../Divider';
3
4
  import Floating from '../Floating/Floating.svelte';
4
5
  import SearchInput from '../SearchInput';
5
6
  import { surfaceStyle, type StyleProps } from '../../../style/surface';
6
7
  import type { Anchor } from '../../../utils/positioning';
7
8
  import type {
8
9
  DropdownAction,
10
+ DropdownDivider,
9
11
  DropdownEntry,
10
12
  DropdownGroup,
11
13
  DropdownItem,
@@ -31,6 +33,8 @@
31
33
  'type' in entry && entry.type === 'group';
32
34
  const isAction = (entry: DropdownItem<T> | DropdownAction): entry is DropdownAction =>
33
35
  'type' in entry && entry.type === 'action';
36
+ const isDivider = (entry: DropdownEntry<T>): entry is DropdownDivider =>
37
+ 'type' in entry && entry.type === 'divider';
34
38
  const isSubmenu = (entry: DropdownEntry<T>): entry is DropdownSubmenu<T> =>
35
39
  'type' in entry && entry.type === 'submenu';
36
40
 
@@ -62,6 +66,10 @@
62
66
  inheritedDisabled = false
63
67
  ): DropdownItem<T>[] =>
64
68
  entries.flatMap((entry) => {
69
+ if (isDivider(entry)) {
70
+ return [];
71
+ }
72
+
65
73
  const nextDisabled = inheritedDisabled || !!entry.disabled;
66
74
  if (isGroup(entry) || isSubmenu(entry)) {
67
75
  return flattenItems(entry.items, nextDisabled);
@@ -79,6 +87,9 @@
79
87
  if (nested) return nested;
80
88
  continue;
81
89
  }
90
+ if (isDivider(entry)) {
91
+ continue;
92
+ }
82
93
  return entry.label;
83
94
  }
84
95
  return null;
@@ -154,7 +165,7 @@
154
165
  aria-haspopup="dialog"
155
166
  onclick={floating.toggle}
156
167
  >
157
- <span>{triggerText}</span>
168
+ <span class="dropdown-trigger-label">{triggerText}</span>
158
169
  <ChevronDownIcon class="size-4 text-zinc-500" />
159
170
  </button>
160
171
  {/snippet}
@@ -168,12 +179,13 @@
168
179
  bind:value={query}
169
180
  placeholder={searchPlaceholder}
170
181
  radiusSource={true}
171
- class="w-64"
172
182
  />
173
183
  {/if}
174
184
  {#snippet renderEntries(entries: DropdownEntry<T>[], inheritedDisabled = false)}
175
- {#each entries as entry, index (`${index}-${entry.type ?? 'option'}-${entry.label}`)}
176
- {#if isGroup(entry)}
185
+ {#each entries as entry, index (`${index}-${entry.type ?? 'option'}-${isDivider(entry) ? 'divider' : entry.label}`)}
186
+ {#if isDivider(entry)}
187
+ <Divider />
188
+ {:else if isGroup(entry)}
177
189
  <div class="dropdown-group vstack">
178
190
  {#if entry.label}
179
191
  <div class="dropdown-group-label">{entry.label}</div>
@@ -274,6 +286,7 @@
274
286
  .dropdown-trigger {
275
287
  display: inline-flex;
276
288
  min-width: 8rem;
289
+ max-width: 100%;
277
290
  align-items: center;
278
291
  border: 1px solid var(--sveltely-border-color);
279
292
  border-radius: var(--sveltely-border-radius);
@@ -286,6 +299,18 @@
286
299
  transition: color 150ms, border-color 150ms, background-color 150ms;
287
300
  }
288
301
 
302
+ .dropdown-trigger-label {
303
+ min-width: 0;
304
+ flex: 1 1 auto;
305
+ overflow: hidden;
306
+ text-overflow: ellipsis;
307
+ white-space: nowrap;
308
+ }
309
+
310
+ .dropdown-trigger :global(svg) {
311
+ flex: 0 0 auto;
312
+ }
313
+
289
314
  .dropdown-trigger:hover {
290
315
  background: var(--sveltely-hover-color);
291
316
  }
@@ -300,6 +325,8 @@
300
325
  }
301
326
 
302
327
  .dropdown-content {
328
+ --dropdown-item-padding-x: calc(var(--sveltely-padding-x) * 0.67);
329
+ --dropdown-item-padding-y: calc(var(--sveltely-padding-y) * 0.33);
303
330
  gap: var(--sveltely-inset);
304
331
  }
305
332
 
@@ -307,11 +334,18 @@
307
334
  gap: var(--sveltely-inset);
308
335
  }
309
336
 
337
+ .dropdown-content :global(.divider) {
338
+ --divider-width: auto;
339
+ margin-inline: var(--dropdown-item-padding-x);
340
+ margin-block: calc(var(--sveltely-inset) * 0.5);
341
+ background: var(--sveltely-border-color);
342
+ }
343
+
310
344
  .dropdown-item {
311
345
  width: 100%;
312
346
  gap: calc(var(--sveltely-inset) * 2);
313
347
  border-radius: var(--dropdown-item-radius, var(--sveltely-border-radius-nested));
314
- padding: calc(var(--sveltely-padding-y) * 0.33) calc(var(--sveltely-padding-x) * 0.67);
348
+ padding: var(--dropdown-item-padding-y) var(--dropdown-item-padding-x);
315
349
  }
316
350
 
317
351
  .dropdown-item :global(*) {
@@ -1,2 +1,2 @@
1
1
  export { default } from './Dropdown.svelte';
2
- export type { DropdownAction, DropdownEntry, DropdownGroup, DropdownItem, DropdownSubmenu } from './types';
2
+ export type { DropdownAction, DropdownDivider, DropdownEntry, DropdownGroup, DropdownItem, DropdownSubmenu } from './types';
@@ -11,6 +11,9 @@ export type DropdownAction = {
11
11
  disabled?: boolean;
12
12
  onSelect: () => void;
13
13
  };
14
+ export type DropdownDivider = {
15
+ type: 'divider';
16
+ };
14
17
  export type DropdownSubmenu<TValue extends string | number = string> = {
15
18
  type: 'submenu';
16
19
  label: string;
@@ -24,4 +27,4 @@ export type DropdownGroup<TValue extends string | number = string> = {
24
27
  disabled?: boolean;
25
28
  items: DropdownEntry<TValue>[];
26
29
  };
27
- export type DropdownEntry<TValue extends string | number = string> = DropdownItem<TValue> | DropdownAction | DropdownGroup<TValue> | DropdownSubmenu<TValue>;
30
+ export type DropdownEntry<TValue extends string | number = string> = DropdownItem<TValue> | DropdownAction | DropdownDivider | DropdownGroup<TValue> | DropdownSubmenu<TValue>;
@@ -177,6 +177,39 @@
177
177
  return Number.isFinite(parsed) ? parsed : 0;
178
178
  };
179
179
 
180
+ const resolveCssLength = (element: HTMLElement, value: string) => {
181
+ const trimmedValue = value.trim();
182
+ if (!trimmedValue) return 0;
183
+ if (trimmedValue.endsWith('px')) return parsePx(trimmedValue);
184
+
185
+ const probe = document.createElement('div');
186
+ probe.style.position = 'absolute';
187
+ probe.style.visibility = 'hidden';
188
+ probe.style.pointerEvents = 'none';
189
+ probe.style.width = trimmedValue;
190
+ element.appendChild(probe);
191
+ const width = probe.getBoundingClientRect().width;
192
+ probe.remove();
193
+ return width;
194
+ };
195
+
196
+ const getFloatingInset = () => {
197
+ if (!panelEl) return 0;
198
+ const styles = getComputedStyle(panelEl);
199
+ return resolveCssLength(panelEl, styles.getPropertyValue('--sveltely-floating-inset'));
200
+ };
201
+
202
+ const offsetCoords = (
203
+ coords: { top: number; left: number },
204
+ currentAnchor: Anchor,
205
+ offset: number
206
+ ) => {
207
+ if (currentAnchor === 'bottom') return { ...coords, top: coords.top + offset };
208
+ if (currentAnchor === 'top') return { ...coords, top: coords.top - offset };
209
+ if (currentAnchor === 'right') return { ...coords, left: coords.left + offset };
210
+ return { ...coords, left: coords.left - offset };
211
+ };
212
+
180
213
  const updateRadiusFromSource = () => {
181
214
  if (!matchPanelRadiusToSource || !panelEl || !contentEl) {
182
215
  computedPanelRadius = null;
@@ -222,7 +255,7 @@
222
255
  placement,
223
256
  preferredAlign
224
257
  );
225
- panelCoords = { top: result.top, left: result.left };
258
+ panelCoords = offsetCoords({ top: result.top, left: result.left }, result.anchor, getFloatingInset());
226
259
  panelTransform = result.transform;
227
260
  resolvedAnchor = result.anchor;
228
261
  };
@@ -317,6 +350,7 @@
317
350
 
318
351
  const resolvedPanelStyle = $derived.by(() => {
319
352
  const declarations = [
353
+ '--sveltely-floating-inset: var(--sveltely-inset);',
320
354
  `top: ${panelCoords.top}px;`,
321
355
  `left: ${panelCoords.left}px;`,
322
356
  `transform: ${panelTransform};`
@@ -0,0 +1,14 @@
1
+ <script lang="ts" generics="T">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ type Props = {
5
+ items: T[];
6
+ children: Snippet<[item: T, index: number]>;
7
+ };
8
+
9
+ let { items, children }: Props = $props();
10
+ </script>
11
+
12
+ {#each items as item, index}
13
+ {@render children(item, index)}
14
+ {/each}
@@ -0,0 +1,28 @@
1
+ import type { Snippet } from 'svelte';
2
+ declare function $$render<T>(): {
3
+ props: {
4
+ items: T[];
5
+ children: Snippet<[item: T, index: number]>;
6
+ };
7
+ exports: {};
8
+ bindings: "";
9
+ slots: {};
10
+ events: {};
11
+ };
12
+ declare class __sveltets_Render<T> {
13
+ props(): ReturnType<typeof $$render<T>>['props'];
14
+ events(): ReturnType<typeof $$render<T>>['events'];
15
+ slots(): ReturnType<typeof $$render<T>>['slots'];
16
+ bindings(): "";
17
+ exports(): {};
18
+ }
19
+ interface $$IsomorphicComponent {
20
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
21
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
22
+ } & ReturnType<__sveltets_Render<T>['exports']>;
23
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
24
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
25
+ }
26
+ declare const ForEach: $$IsomorphicComponent;
27
+ type ForEach<T> = InstanceType<typeof ForEach<T>>;
28
+ export default ForEach;
@@ -0,0 +1 @@
1
+ export { default } from './ForEach.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './ForEach.svelte';
@@ -0,0 +1,74 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
4
+ import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
5
+
6
+ type Props = {
7
+ children?: Snippet;
8
+ columns?: number | string;
9
+ rows?: number | string;
10
+ autoRows?: number | string;
11
+ dense?: boolean;
12
+ } & LayoutProps &
13
+ StyleProps;
14
+
15
+ let {
16
+ children,
17
+ columns = 1,
18
+ rows,
19
+ autoRows,
20
+ dense = false,
21
+ ...restProps
22
+ }: Props = $props();
23
+
24
+ const extractedLayoutProps = $derived.by(() => extractLayoutProps(restProps));
25
+ const layoutProps = $derived(extractedLayoutProps.layoutProps);
26
+ const afterLayoutProps = $derived(extractedLayoutProps.restProps);
27
+ const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
28
+ const styleProps = $derived(extractedStyleProps.styleProps);
29
+ const templateColumns = $derived(
30
+ typeof columns === 'number' ? `repeat(${columns}, minmax(0, 1fr))` : columns
31
+ );
32
+ const templateRows = $derived(
33
+ rows === undefined ? undefined : typeof rows === 'number' ? `repeat(${rows}, minmax(0, 1fr))` : rows
34
+ );
35
+ const gridAutoRows = $derived(
36
+ autoRows === undefined ? undefined : typeof autoRows === 'number' ? `${autoRows}rem` : autoRows
37
+ );
38
+ const rootStyle = $derived.by(() =>
39
+ [
40
+ `grid-template-columns: ${templateColumns};`,
41
+ templateRows ? `grid-template-rows: ${templateRows};` : '',
42
+ gridAutoRows ? `grid-auto-rows: ${gridAutoRows};` : '',
43
+ layoutStyle(layoutProps),
44
+ surfaceStyle(styleProps, 'grid')
45
+ ]
46
+ .filter(Boolean)
47
+ .join(' ')
48
+ );
49
+ </script>
50
+
51
+ <div class="grid" class:grid-dense={dense} style={rootStyle}>
52
+ {#if children}
53
+ {@render children()}
54
+ {/if}
55
+ </div>
56
+
57
+ <style>
58
+ .grid {
59
+ display: grid;
60
+ min-width: 0;
61
+ min-height: 0;
62
+ align-items: stretch;
63
+ gap: var(--grid-gap, 0px);
64
+ border-radius: var(--grid-border-radius, 0px);
65
+ padding:
66
+ var(--grid-padding-y, 0px)
67
+ var(--grid-padding-x, 0px);
68
+ font-size: var(--grid-font-size, inherit);
69
+ }
70
+
71
+ .grid-dense {
72
+ grid-auto-flow: dense;
73
+ }
74
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type LayoutProps } from '../../../style/layout';
3
+ import { type StyleProps } from '../../../style/surface';
4
+ type Props = {
5
+ children?: Snippet;
6
+ columns?: number | string;
7
+ rows?: number | string;
8
+ autoRows?: number | string;
9
+ dense?: boolean;
10
+ } & LayoutProps & StyleProps;
11
+ declare const Grid: import("svelte").Component<Props, {}, "">;
12
+ type Grid = ReturnType<typeof Grid>;
13
+ export default Grid;