@svelte-thing/primitives 0.0.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 (137) hide show
  1. package/README.md +1 -0
  2. package/dist/button/index.d.mts +9 -0
  3. package/dist/button/index.mjs +6 -0
  4. package/dist/chunk-D7D4PA-g.mjs +13 -0
  5. package/dist/context-BJAwVB1S.mjs +5 -0
  6. package/dist/context-D77KIx26.mjs +5 -0
  7. package/dist/context-DwEoOJU2.mjs +5 -0
  8. package/dist/createInput.svelte-DTz79zNF.d.mts +9 -0
  9. package/dist/createInput.svelte-DzcT9u1t.mjs +92 -0
  10. package/dist/field/date/index.d.mts +2 -0
  11. package/dist/field/date/index.mjs +27 -0
  12. package/dist/field/error/index.d.mts +2 -0
  13. package/dist/field/error/index.mjs +10 -0
  14. package/dist/field/index.d.mts +13 -0
  15. package/dist/field/index.mjs +25 -0
  16. package/dist/field/input/index.d.mts +2 -0
  17. package/dist/field/input/index.mjs +2 -0
  18. package/dist/field/label/index.d.mts +2 -0
  19. package/dist/field/label/index.mjs +15 -0
  20. package/dist/field/password/index.d.mts +2 -0
  21. package/dist/field/password/index.mjs +61 -0
  22. package/dist/field/root/index.d.mts +2 -0
  23. package/dist/field/root/index.mjs +79 -0
  24. package/dist/index-07sbgO0w.d.mts +9 -0
  25. package/dist/index-B-PBb_8h.d.mts +14 -0
  26. package/dist/index-BLb5EVhG.d.mts +35 -0
  27. package/dist/index-BVVgDSdq.d.mts +1 -0
  28. package/dist/index-Boej-mdy.d.mts +25 -0
  29. package/dist/index-C-ME1Tk9.d.mts +9 -0
  30. package/dist/index-CA_POIj_.d.mts +9 -0
  31. package/dist/index-CbchmhrC.d.mts +9 -0
  32. package/dist/index-D2vzMU8o.d.mts +13 -0
  33. package/dist/index-DAHEDQQS.d.mts +9 -0
  34. package/dist/index-OI9KlY5x.d.mts +9 -0
  35. package/dist/index-j-8eH9nJ.d.mts +29 -0
  36. package/dist/index.d.mts +3 -0
  37. package/dist/index.mjs +3 -0
  38. package/dist/menu/index.d.mts +5 -0
  39. package/dist/menu/index.mjs +6 -0
  40. package/dist/menu/item/index.d.mts +14 -0
  41. package/dist/menu/item/index.mjs +86 -0
  42. package/dist/menu/popup/index.d.mts +19 -0
  43. package/dist/menu/popup/index.mjs +121 -0
  44. package/dist/menu/root/index.d.mts +28 -0
  45. package/dist/menu/root/index.mjs +69 -0
  46. package/dist/menu/trigger/index.d.mts +13 -0
  47. package/dist/menu/trigger/index.mjs +27 -0
  48. package/dist/pagination/content/index.d.mts +9 -0
  49. package/dist/pagination/content/index.mjs +6 -0
  50. package/dist/pagination/ellipsis/index.d.mts +9 -0
  51. package/dist/pagination/ellipsis/index.mjs +6 -0
  52. package/dist/pagination/index.d.mts +6 -0
  53. package/dist/pagination/index.mjs +6 -0
  54. package/dist/pagination/item/index.d.mts +9 -0
  55. package/dist/pagination/item/index.mjs +6 -0
  56. package/dist/pagination/link/index.d.mts +13 -0
  57. package/dist/pagination/link/index.mjs +13 -0
  58. package/dist/pagination/root/index.d.mts +9 -0
  59. package/dist/pagination/root/index.mjs +9 -0
  60. package/dist/tab/index.d.mts +13 -0
  61. package/dist/tab/index.mjs +21 -0
  62. package/dist/tab/indicator/index.d.mts +2 -0
  63. package/dist/tab/indicator/index.mjs +42 -0
  64. package/dist/tab/list/index.d.mts +2 -0
  65. package/dist/tab/list/index.mjs +20 -0
  66. package/dist/tab/panel/index.d.mts +2 -0
  67. package/dist/tab/panel/index.mjs +28 -0
  68. package/dist/tab/root/index.d.mts +2 -0
  69. package/dist/tab/root/index.mjs +57 -0
  70. package/dist/tab/title/index.d.mts +2 -0
  71. package/dist/tab/title/index.mjs +18 -0
  72. package/dist/tab/trigger/index.d.mts +2 -0
  73. package/dist/tab/trigger/index.mjs +94 -0
  74. package/dist/table/index.d.mts +2 -0
  75. package/dist/table/index.mjs +2 -0
  76. package/dist/table/row/index.d.mts +17 -0
  77. package/dist/table/row/index.mjs +17 -0
  78. package/package.json +64 -0
  79. package/src/button/createButton.svelte.ts +11 -0
  80. package/src/button/index.ts +1 -0
  81. package/src/field/date/createPicker.svelte.ts +41 -0
  82. package/src/field/date/index.ts +1 -0
  83. package/src/field/error/createError.svelte.ts +21 -0
  84. package/src/field/error/index.ts +1 -0
  85. package/src/field/index.ts +6 -0
  86. package/src/field/input/createInput.svelte.ts +136 -0
  87. package/src/field/input/index.ts +1 -0
  88. package/src/field/label/createLabel.svelte.ts +24 -0
  89. package/src/field/label/index.ts +1 -0
  90. package/src/field/password/context.ts +5 -0
  91. package/src/field/password/createPassword.svelte.ts +43 -0
  92. package/src/field/password/createShow.svelte.ts +47 -0
  93. package/src/field/password/index.ts +3 -0
  94. package/src/field/root/context.ts +5 -0
  95. package/src/field/root/createRoot.svelte.ts +113 -0
  96. package/src/field/root/index.ts +2 -0
  97. package/src/index.ts +2 -0
  98. package/src/menu/index.ts +4 -0
  99. package/src/menu/item/createItem.svelte.ts +123 -0
  100. package/src/menu/item/index.ts +1 -0
  101. package/src/menu/popup/createPopup.svelte.ts +186 -0
  102. package/src/menu/popup/index.ts +1 -0
  103. package/src/menu/root/context.ts +5 -0
  104. package/src/menu/root/createRoot.svelte.ts +96 -0
  105. package/src/menu/root/index.ts +2 -0
  106. package/src/menu/trigger/createTrigger.svelte.ts +44 -0
  107. package/src/menu/trigger/index.ts +1 -0
  108. package/src/pagination/content/createContent.svelte.ts +11 -0
  109. package/src/pagination/content/index.ts +1 -0
  110. package/src/pagination/ellipsis/createEllipsis.svelte.ts +13 -0
  111. package/src/pagination/ellipsis/index.ts +1 -0
  112. package/src/pagination/index.ts +5 -0
  113. package/src/pagination/item/createItem.svelte.ts +11 -0
  114. package/src/pagination/item/index.ts +1 -0
  115. package/src/pagination/link/createLink.svelte.ts +23 -0
  116. package/src/pagination/link/index.ts +1 -0
  117. package/src/pagination/root/createRoot.svelte.ts +14 -0
  118. package/src/pagination/root/index.ts +1 -0
  119. package/src/style.css +10 -0
  120. package/src/tab/index.ts +6 -0
  121. package/src/tab/indicator/createIndicator.svelte.ts +79 -0
  122. package/src/tab/indicator/index.ts +1 -0
  123. package/src/tab/list/createList.svelte.ts +27 -0
  124. package/src/tab/list/index.ts +1 -0
  125. package/src/tab/panel/createPanel.svelte.ts +42 -0
  126. package/src/tab/panel/index.ts +1 -0
  127. package/src/tab/root/context.ts +5 -0
  128. package/src/tab/root/createRoot.svelte.ts +85 -0
  129. package/src/tab/root/index.ts +2 -0
  130. package/src/tab/title/createTitle.svelte.ts +27 -0
  131. package/src/tab/title/index.ts +1 -0
  132. package/src/tab/trigger/createTrigger.svelte.ts +129 -0
  133. package/src/tab/trigger/index.ts +1 -0
  134. package/src/table/index.ts +1 -0
  135. package/src/table/row/context.ts +5 -0
  136. package/src/table/row/createRow.svelte.ts +26 -0
  137. package/src/table/row/index.ts +2 -0
@@ -0,0 +1,113 @@
1
+ import type { Exact } from '@svelte-thing/type-utils';
2
+ import { uid } from 'uid';
3
+
4
+ import { setRoot } from './context';
5
+
6
+ export interface CreateRootProps {
7
+ id: string | null | undefined;
8
+ isDirty: boolean | null | undefined;
9
+ isDisabled: boolean | null | undefined;
10
+ isFilled: boolean | null | undefined;
11
+ isFocused: boolean | null | undefined;
12
+ isInvalid: boolean | null | undefined;
13
+ isTouched: boolean | null | undefined;
14
+ isValid: boolean | null | undefined;
15
+ }
16
+
17
+ export interface RootState {
18
+ id: string;
19
+ labelId: string;
20
+ controlId: string;
21
+ errorId: string;
22
+ isDirty: boolean;
23
+ isDisabled: boolean;
24
+ isFilled: boolean;
25
+ isFocused: boolean;
26
+ isInvalid: boolean;
27
+ isTouched: boolean;
28
+ isValid: boolean;
29
+ props: {
30
+ id: string;
31
+ };
32
+ }
33
+
34
+ export function createRoot<T extends CreateRootProps>(props: Exact<CreateRootProps, T>): RootState {
35
+ const id = $derived(props.id ?? uid());
36
+ const labelId = $derived(`${id}-label`);
37
+ const controlId = $derived(`${id}-control`);
38
+ const errorId = $derived(`${id}-error`);
39
+
40
+ let isDirty = $derived(props.isDirty ?? false);
41
+ let isDisabled = $derived(props.isDisabled ?? false);
42
+ let isFilled = $derived(props.isFilled ?? false);
43
+ let isFocused = $derived(props.isFocused ?? false);
44
+ let isInvalid = $derived(props.isInvalid ?? false);
45
+ let isTouched = $derived(props.isTouched ?? false);
46
+ let isValid = $derived(props.isValid ?? false);
47
+
48
+ const root: RootState = {
49
+ get id() {
50
+ return id;
51
+ },
52
+ get labelId() {
53
+ return labelId;
54
+ },
55
+ get controlId() {
56
+ return controlId;
57
+ },
58
+ get errorId() {
59
+ return errorId;
60
+ },
61
+ get isDirty() {
62
+ return isDirty;
63
+ },
64
+ set isDirty(value) {
65
+ isDirty = value;
66
+ },
67
+ get isDisabled() {
68
+ return isDisabled;
69
+ },
70
+ set isDisabled(value) {
71
+ isDisabled = value;
72
+ },
73
+ get isFilled() {
74
+ return isFilled;
75
+ },
76
+ set isFilled(value) {
77
+ isFilled = value;
78
+ },
79
+ get isFocused() {
80
+ return isFocused;
81
+ },
82
+ set isFocused(value) {
83
+ isFocused = value;
84
+ },
85
+ get isInvalid() {
86
+ return isInvalid;
87
+ },
88
+ set isInvalid(value) {
89
+ isInvalid = value;
90
+ },
91
+ get isTouched() {
92
+ return isTouched;
93
+ },
94
+ set isTouched(value) {
95
+ isTouched = value;
96
+ },
97
+ get isValid() {
98
+ return isValid;
99
+ },
100
+ set isValid(value) {
101
+ isValid = value;
102
+ },
103
+ props: {
104
+ get id() {
105
+ return id;
106
+ },
107
+ },
108
+ };
109
+
110
+ setRoot(root);
111
+
112
+ return root;
113
+ }
@@ -0,0 +1,2 @@
1
+ export * from './createRoot.svelte.js';
2
+ export * from './context.js';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * as Field from './field/index.js';
2
+ export * as Tab from './tab/index.js';
@@ -0,0 +1,4 @@
1
+ export * from './item/index.js';
2
+ export * from './popup/index.js';
3
+ export * from './root/index.js';
4
+ export * from './trigger/index.js';
@@ -0,0 +1,123 @@
1
+ import type { Exact } from '@svelte-thing/type-utils';
2
+ import { flushSync } from 'svelte';
3
+ import { createAttachmentKey } from 'svelte/attachments';
4
+ import type { HTMLAttributes } from 'svelte/elements';
5
+ import { uid } from 'uid';
6
+
7
+ import { getRoot } from '../root/context.js';
8
+
9
+ export interface CreateItemProps {
10
+ disabled: boolean | null | undefined;
11
+ id: string | null | undefined;
12
+ }
13
+
14
+ export interface ItemState {
15
+ props: HTMLAttributes<HTMLElement>;
16
+ }
17
+
18
+ export function createItem<T extends CreateItemProps>(props: Exact<CreateItemProps, T>): ItemState {
19
+ const root = getRoot();
20
+ const itemId = $derived(props.id ?? `menuitem-${uid()}`);
21
+
22
+ return {
23
+ props: {
24
+ 'data-menu-item': '',
25
+ get 'aria-disabled'() {
26
+ return props.disabled ? 'true' : undefined;
27
+ },
28
+ get 'data-disabled'() {
29
+ return props.disabled ? '' : undefined;
30
+ },
31
+ get 'data-id'() {
32
+ return itemId;
33
+ },
34
+ get 'data-root-id'() {
35
+ return root.id;
36
+ },
37
+ get id() {
38
+ return itemId;
39
+ },
40
+ get tabindex() {
41
+ return root.highlightedItemId === itemId ? 0 : -1;
42
+ },
43
+ onkeydown(event) {
44
+ if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) {
45
+ return;
46
+ }
47
+
48
+ let preventDefault = false;
49
+ let pendingHighlighted: string | null = null;
50
+
51
+ const item = event.currentTarget as HTMLElement;
52
+ const items = [
53
+ ...document.querySelectorAll<HTMLElement>(
54
+ `[data-menu-item][data-root-id="${root.id}"]:not([data-disabled])`,
55
+ ),
56
+ ];
57
+ const currentIndex = items.indexOf(item);
58
+
59
+ const nextKeyName = root.orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
60
+ const previousKeyName = root.orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
61
+
62
+ switch (event.key) {
63
+ case previousKeyName: {
64
+ const nextIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1;
65
+ pendingHighlighted = items[nextIndex]?.dataset.id ?? null;
66
+ preventDefault = true;
67
+ break;
68
+ }
69
+ case nextKeyName: {
70
+ const nextIndex = (currentIndex + 1) % items.length;
71
+ pendingHighlighted = items[nextIndex]?.dataset.id ?? null;
72
+ preventDefault = true;
73
+ break;
74
+ }
75
+ case 'Home': {
76
+ pendingHighlighted = items[0]?.dataset.id ?? null;
77
+ preventDefault = true;
78
+ break;
79
+ }
80
+ case 'End': {
81
+ pendingHighlighted = items.at(-1)?.dataset.id ?? null;
82
+ preventDefault = true;
83
+ break;
84
+ }
85
+ default: {
86
+ break;
87
+ }
88
+ }
89
+
90
+ if (pendingHighlighted !== null) {
91
+ root.highlightedItemId = pendingHighlighted;
92
+ flushSync(); // set tabindex to 0
93
+ items.find((el) => el.dataset.id === pendingHighlighted)?.focus();
94
+ }
95
+
96
+ if (preventDefault) {
97
+ event.stopPropagation();
98
+ event.preventDefault();
99
+ }
100
+ },
101
+ onmouseover: (event) => {
102
+ if (root.shouldHighlightOnHover) {
103
+ root.highlightedItemId = itemId;
104
+ flushSync(); // set tabindex to 0
105
+ event.currentTarget.focus();
106
+ }
107
+ },
108
+ onmouseleave: () => {
109
+ if (root.shouldHighlightOnHover && root.highlightedItemId === itemId) {
110
+ root.highlightedItemId = null;
111
+ }
112
+ },
113
+ role: 'menuitem',
114
+ [createAttachmentKey()]: () => {
115
+ return () => {
116
+ if (root.highlightedItemId === itemId) {
117
+ root.highlightedItemId = null;
118
+ }
119
+ };
120
+ },
121
+ },
122
+ };
123
+ }
@@ -0,0 +1 @@
1
+ export * from './createItem.svelte.js';
@@ -0,0 +1,186 @@
1
+ import { assertNever } from '@svelte-thing/type-utils';
2
+ import { flushSync, untrack } from 'svelte';
3
+ import { createAttachmentKey } from 'svelte/attachments';
4
+ import type { HTMLAttributes } from 'svelte/elements';
5
+ import { on } from 'svelte/events';
6
+
7
+ import { getRoot } from '../root/context.js';
8
+
9
+ type Side = 'left' | 'right' | 'bottom' | 'top' | 'inline-end' | 'inline-start';
10
+ type Align = 'start' | 'end' | 'center';
11
+
12
+ export interface CreatePopupProps {
13
+ popover: HTMLAttributes<HTMLElement>['popover'] | null | undefined;
14
+ align: Align | undefined;
15
+ side: Side | undefined;
16
+ alignOffset: number | undefined;
17
+ positionAnchor: string | null | undefined;
18
+ sideOffset: number | undefined;
19
+ }
20
+
21
+ export interface PopupState {
22
+ props: HTMLAttributes<HTMLElement>;
23
+ }
24
+
25
+ export function createPopup(props: CreatePopupProps): PopupState {
26
+ const root = getRoot();
27
+ return {
28
+ props: {
29
+ 'data-menu-popup': '',
30
+ get 'data-root-id'() {
31
+ return root.id;
32
+ },
33
+ get 'data-open'() {
34
+ return root.isOpen ? '' : undefined;
35
+ },
36
+ get 'data-closed'() {
37
+ return root.isOpen ? undefined : '';
38
+ },
39
+ get 'data-side'() {
40
+ return props.side ?? 'bottom';
41
+ },
42
+ get 'data-align'() {
43
+ return props.align ?? 'center';
44
+ },
45
+ get popover() {
46
+ return props.popover ?? '';
47
+ },
48
+ get id() {
49
+ return root.popupId;
50
+ },
51
+ get style() {
52
+ const positionAnchor =
53
+ props.positionAnchor === undefined ? root.anchorName : props.positionAnchor;
54
+ const styles = [
55
+ positionAnchor === null ? undefined : `position-anchor: ${positionAnchor}`,
56
+ `justify-self: anchor-${props.align ?? 'center'}`,
57
+ ];
58
+
59
+ const side = props.side ?? 'bottom';
60
+ const sideOffset = props.sideOffset ?? 0;
61
+ if (side === 'bottom') {
62
+ styles.push(`top: calc(anchor(bottom) + ${sideOffset}px);`);
63
+ } else if (side === 'top') {
64
+ styles.push(`bottom: calc(anchor(top) + ${sideOffset}px);`);
65
+ } else if (side === 'left') {
66
+ styles.push(`right: calc(anchor(left) + ${sideOffset}px);`);
67
+ } else if (side === 'right') {
68
+ styles.push(`left: calc(anchor(right) + ${sideOffset}px);`);
69
+ } else if (side === 'inline-end') {
70
+ styles.push(`inset-inline-end: calc(anchor(end) + ${sideOffset}px);`);
71
+ } else if (side === 'inline-start') {
72
+ styles.push(`inset-inline-start: calc(anchor(start) + ${sideOffset}px);`);
73
+ } else {
74
+ assertNever(side);
75
+ }
76
+
77
+ return styles.filter(Boolean).join('; ');
78
+ },
79
+ get tabindex() {
80
+ return root.isOpen ? -1 : undefined;
81
+ },
82
+ [createAttachmentKey()](element) {
83
+ const update = (event?: ToggleEvent) => {
84
+ if (event && event.newState !== 'open') {
85
+ return;
86
+ }
87
+
88
+ // set `data-open` and `tabindex` attributes before the popover becomes visible
89
+ root.isOpen = event ? true : element.matches(':popover-open');
90
+ flushSync();
91
+ if (root.isOpen) {
92
+ element.focus();
93
+ }
94
+ };
95
+ const open = () => {
96
+ element.showPopover();
97
+ };
98
+ const close = () => {
99
+ element.hidePopover();
100
+ };
101
+
102
+ root.open = open;
103
+ root.close = close;
104
+
105
+ $effect(() => {
106
+ if (root._propIsOpen === true && !element.matches(':popover-open')) {
107
+ element.showPopover();
108
+ } else if (root._propIsOpen === false && element.matches(':popover-open')) {
109
+ element.hidePopover();
110
+ }
111
+ untrack(() => {
112
+ update();
113
+ });
114
+ });
115
+
116
+ update();
117
+
118
+ const offBefore = on(element, 'beforetoggle', update);
119
+ const offAfter = on(element, 'toggle', update);
120
+
121
+ return () => {
122
+ offBefore();
123
+ offAfter();
124
+ root.open = () => {
125
+ throw new Error('Attempted to open before popup initialization');
126
+ };
127
+ root.close = () => {
128
+ throw new Error('Attempted to close before popup initialization');
129
+ };
130
+ };
131
+ },
132
+ onkeydown(event) {
133
+ if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) {
134
+ return;
135
+ }
136
+
137
+ const popup = event.currentTarget as HTMLElement;
138
+ if (document.activeElement !== popup) {
139
+ return;
140
+ }
141
+
142
+ let preventDefault = false;
143
+ let pendingHighlighted: string | null = null;
144
+ const items = [
145
+ ...document.querySelectorAll<HTMLElement>(
146
+ `[data-menu-item][data-root-id="${root.id}"]:not([data-disabled])`,
147
+ ),
148
+ ];
149
+
150
+ const nextKeyName = root.orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
151
+ const previousKeyName = root.orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
152
+
153
+ switch (event.key) {
154
+ case previousKeyName:
155
+ case 'End': {
156
+ pendingHighlighted = items.at(-1)?.dataset.id ?? null;
157
+ preventDefault = true;
158
+ break;
159
+ }
160
+ case nextKeyName:
161
+ case 'Home': {
162
+ pendingHighlighted = items[0]?.dataset.id ?? null;
163
+ preventDefault = true;
164
+ break;
165
+ }
166
+
167
+ default: {
168
+ break;
169
+ }
170
+ }
171
+
172
+ if (pendingHighlighted !== null) {
173
+ root.highlightedItemId = pendingHighlighted;
174
+ flushSync(); // set tabindex to 0
175
+ items.find((el) => el.dataset.id === pendingHighlighted)?.focus();
176
+ }
177
+
178
+ if (preventDefault) {
179
+ event.stopPropagation();
180
+ event.preventDefault();
181
+ }
182
+ },
183
+ role: 'menu',
184
+ },
185
+ };
186
+ }
@@ -0,0 +1 @@
1
+ export * from './createPopup.svelte.js';
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'svelte';
2
+
3
+ import type { RootState } from './createRoot.svelte.js';
4
+
5
+ export const [getRoot, setRoot] = createContext<RootState>();
@@ -0,0 +1,96 @@
1
+ import { uid } from 'uid';
2
+
3
+ import { setRoot } from './context.js';
4
+
5
+ export type Orientation = 'horizontal' | 'vertical';
6
+
7
+ export interface CreateRootProps {
8
+ anchorName: string | undefined;
9
+ id: string | null | undefined;
10
+ isOpen: boolean | undefined;
11
+ orientation: Orientation | undefined;
12
+ shouldHighlightOnHover: boolean | undefined;
13
+ }
14
+
15
+ export interface RootState {
16
+ anchorName: string;
17
+ close: () => void;
18
+ highlightedItemId: string | null;
19
+ id: string;
20
+ isOpen: boolean;
21
+ open: () => void;
22
+ orientation: Orientation;
23
+ popupId: string;
24
+ shouldHighlightOnHover: boolean;
25
+ triggerId: string;
26
+ _propIsOpen: boolean | undefined;
27
+ }
28
+
29
+ export function createRoot(props: CreateRootProps): RootState {
30
+ const id = $derived(props.id ?? uid());
31
+ const anchorName = $derived(props.anchorName ?? `--${id}-anchor`);
32
+ const popupId = $derived(`${id}-popup`);
33
+ const triggerId = $derived(`${id}-trigger`);
34
+ const orientation = $derived(props.orientation ?? 'vertical');
35
+
36
+ let isOpen = $state(false);
37
+ let highlightedItemId = $state<string | null>(null);
38
+ let open = () => {
39
+ throw new Error('Attempted to open before popup initialization');
40
+ };
41
+ let close = () => {
42
+ throw new Error('Attempted to close before popup initialization');
43
+ };
44
+
45
+ const root: RootState = {
46
+ get anchorName() {
47
+ return anchorName;
48
+ },
49
+ get highlightedItemId() {
50
+ return highlightedItemId;
51
+ },
52
+ set highlightedItemId(value) {
53
+ highlightedItemId = value;
54
+ },
55
+ get id() {
56
+ return id;
57
+ },
58
+ get isOpen() {
59
+ return isOpen;
60
+ },
61
+ set isOpen(value) {
62
+ isOpen = value;
63
+ },
64
+ get orientation() {
65
+ return orientation;
66
+ },
67
+ get popupId() {
68
+ return popupId;
69
+ },
70
+ get shouldHighlightOnHover() {
71
+ return props.shouldHighlightOnHover ?? true;
72
+ },
73
+ get _propIsOpen() {
74
+ return props.isOpen;
75
+ },
76
+ get triggerId() {
77
+ return triggerId;
78
+ },
79
+ get close() {
80
+ return close;
81
+ },
82
+ set close(value) {
83
+ close = value;
84
+ },
85
+ get open() {
86
+ return open;
87
+ },
88
+ set open(value) {
89
+ open = value;
90
+ },
91
+ };
92
+
93
+ setRoot(root);
94
+
95
+ return root;
96
+ }
@@ -0,0 +1,2 @@
1
+ export * from './context.js';
2
+ export * from './createRoot.svelte.js';
@@ -0,0 +1,44 @@
1
+ import type { Exact } from '@svelte-thing/type-utils';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+
4
+ import { getRoot } from '../root/context.js';
5
+
6
+ export interface CreateTriggerProps {
7
+ anchorName: string | null | undefined;
8
+ }
9
+
10
+ export interface TriggerState {
11
+ props: HTMLButtonAttributes;
12
+ }
13
+
14
+ export function createTrigger<T extends CreateTriggerProps>(
15
+ props: Exact<CreateTriggerProps, T>,
16
+ ): TriggerState {
17
+ const root = getRoot();
18
+
19
+ return {
20
+ props: {
21
+ 'data-menu-trigger': '',
22
+ get 'data-root-id'() {
23
+ return root.id;
24
+ },
25
+ get commandfor() {
26
+ return root.popupId;
27
+ },
28
+ get command() {
29
+ return 'toggle-popover';
30
+ },
31
+ get id() {
32
+ return root.triggerId;
33
+ },
34
+ get style() {
35
+ const anchorName = props.anchorName === undefined ? root.anchorName : props.anchorName;
36
+ if (anchorName === null) {
37
+ return;
38
+ }
39
+
40
+ return `anchor-name: ${anchorName};`;
41
+ },
42
+ },
43
+ };
44
+ }
@@ -0,0 +1 @@
1
+ export * from './createTrigger.svelte.js';
@@ -0,0 +1,11 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+
3
+ export interface ContentState {
4
+ props: HTMLAttributes<HTMLUListElement>;
5
+ }
6
+
7
+ export function createContent(): ContentState {
8
+ return {
9
+ props: {},
10
+ };
11
+ }
@@ -0,0 +1 @@
1
+ export * from './createContent.svelte.js';
@@ -0,0 +1,13 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+
3
+ export interface EllipsisState {
4
+ props: HTMLAttributes<HTMLSpanElement>;
5
+ }
6
+
7
+ export function createEllipsis(): EllipsisState {
8
+ return {
9
+ props: {
10
+ 'aria-hidden': true,
11
+ },
12
+ };
13
+ }
@@ -0,0 +1 @@
1
+ export * from './createEllipsis.svelte.js';
@@ -0,0 +1,5 @@
1
+ export * from './content/index.js';
2
+ export * from './ellipsis/index.js';
3
+ export * from './item/index.js';
4
+ export * from './link/index.js';
5
+ export * from './root/index.js';
@@ -0,0 +1,11 @@
1
+ import type { HTMLLiAttributes } from 'svelte/elements';
2
+
3
+ export interface ItemState {
4
+ props: HTMLLiAttributes;
5
+ }
6
+
7
+ export function createItem(): ItemState {
8
+ return {
9
+ props: {},
10
+ };
11
+ }
@@ -0,0 +1 @@
1
+ export * from './createItem.svelte.js';
@@ -0,0 +1,23 @@
1
+ import type { Exact } from '@svelte-thing/type-utils';
2
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
3
+
4
+ export interface CreateLinkProps {
5
+ isActive?: boolean | null | undefined;
6
+ }
7
+
8
+ export interface LinkState {
9
+ props: HTMLAnchorAttributes;
10
+ }
11
+
12
+ export function createLink<T extends CreateLinkProps>(props: Exact<CreateLinkProps, T>): LinkState {
13
+ return {
14
+ props: {
15
+ get 'aria-current'() {
16
+ return props.isActive ? 'page' : undefined;
17
+ },
18
+ get 'data-active'() {
19
+ return props.isActive;
20
+ },
21
+ },
22
+ };
23
+ }
@@ -0,0 +1 @@
1
+ export * from './createLink.svelte.js';
@@ -0,0 +1,14 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+
3
+ export interface RootState {
4
+ props: HTMLAttributes<HTMLElement>;
5
+ }
6
+
7
+ export function createRoot(): RootState {
8
+ return {
9
+ props: {
10
+ 'aria-label': 'pagination',
11
+ role: 'navigation',
12
+ },
13
+ };
14
+ }
@@ -0,0 +1 @@
1
+ export * from './createRoot.svelte.js';