ajo-ui 0.1.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 (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
package/package.json ADDED
@@ -0,0 +1,250 @@
1
+ {
2
+ "name": "ajo-ui",
3
+ "version": "0.1.0",
4
+ "description": "Unstyled accessible UI components for Ajo",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "src",
10
+ "README.md"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./src/index.ts",
15
+ "default": "./dist/index.js",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./utils": {
19
+ "types": "./src/utils.ts",
20
+ "default": "./dist/utils.js",
21
+ "import": "./dist/utils.js"
22
+ },
23
+ "./accordion": {
24
+ "types": "./src/accordion.tsx",
25
+ "default": "./dist/accordion.js",
26
+ "import": "./dist/accordion.js"
27
+ },
28
+ "./avatar": {
29
+ "types": "./src/avatar.tsx",
30
+ "default": "./dist/avatar.js",
31
+ "import": "./dist/avatar.js"
32
+ },
33
+ "./calendar": {
34
+ "types": "./src/calendar.tsx",
35
+ "default": "./dist/calendar.js",
36
+ "import": "./dist/calendar.js"
37
+ },
38
+ "./carousel": {
39
+ "types": "./src/carousel.tsx",
40
+ "default": "./dist/carousel.js",
41
+ "import": "./dist/carousel.js"
42
+ },
43
+ "./chart": {
44
+ "types": "./src/chart.tsx",
45
+ "default": "./dist/chart.js",
46
+ "import": "./dist/chart.js"
47
+ },
48
+ "./checkbox": {
49
+ "types": "./src/checkbox.tsx",
50
+ "default": "./dist/checkbox.js",
51
+ "import": "./dist/checkbox.js"
52
+ },
53
+ "./checkbox-group": {
54
+ "types": "./src/checkbox-group.tsx",
55
+ "default": "./dist/checkbox-group.js",
56
+ "import": "./dist/checkbox-group.js"
57
+ },
58
+ "./collapsible": {
59
+ "types": "./src/collapsible.tsx",
60
+ "default": "./dist/collapsible.js",
61
+ "import": "./dist/collapsible.js"
62
+ },
63
+ "./command": {
64
+ "types": "./src/command.tsx",
65
+ "default": "./dist/command.js",
66
+ "import": "./dist/command.js"
67
+ },
68
+ "./context-menu": {
69
+ "types": "./src/context-menu.tsx",
70
+ "default": "./dist/context-menu.js",
71
+ "import": "./dist/context-menu.js"
72
+ },
73
+ "./data-table": {
74
+ "types": "./src/data-table.tsx",
75
+ "default": "./dist/data-table.js",
76
+ "import": "./dist/data-table.js"
77
+ },
78
+ "./dialog": {
79
+ "types": "./src/dialog.tsx",
80
+ "default": "./dist/dialog.js",
81
+ "import": "./dist/dialog.js"
82
+ },
83
+ "./direction": {
84
+ "types": "./src/direction.tsx",
85
+ "default": "./dist/direction.js",
86
+ "import": "./dist/direction.js"
87
+ },
88
+ "./drawer": {
89
+ "types": "./src/drawer.tsx",
90
+ "default": "./dist/drawer.js",
91
+ "import": "./dist/drawer.js"
92
+ },
93
+ "./field": {
94
+ "types": "./src/field.tsx",
95
+ "default": "./dist/field.js",
96
+ "import": "./dist/field.js"
97
+ },
98
+ "./input-date": {
99
+ "types": "./src/input-date.tsx",
100
+ "default": "./dist/input-date.js",
101
+ "import": "./dist/input-date.js"
102
+ },
103
+ "./input-group": {
104
+ "types": "./src/input-group.tsx",
105
+ "default": "./dist/input-group.js",
106
+ "import": "./dist/input-group.js"
107
+ },
108
+ "./input-otp": {
109
+ "types": "./src/input-otp.tsx",
110
+ "default": "./dist/input-otp.js",
111
+ "import": "./dist/input-otp.js"
112
+ },
113
+ "./menu": {
114
+ "types": "./src/menu.tsx",
115
+ "default": "./dist/menu.js",
116
+ "import": "./dist/menu.js"
117
+ },
118
+ "./menubar": {
119
+ "types": "./src/menubar.tsx",
120
+ "default": "./dist/menubar.js",
121
+ "import": "./dist/menubar.js"
122
+ },
123
+ "./message-scroller": {
124
+ "types": "./src/message-scroller.tsx",
125
+ "default": "./dist/message-scroller.js",
126
+ "import": "./dist/message-scroller.js"
127
+ },
128
+ "./navigation-menu": {
129
+ "types": "./src/navigation-menu.tsx",
130
+ "default": "./dist/navigation-menu.js",
131
+ "import": "./dist/navigation-menu.js"
132
+ },
133
+ "./popover": {
134
+ "types": "./src/popover.tsx",
135
+ "default": "./dist/popover.js",
136
+ "import": "./dist/popover.js"
137
+ },
138
+ "./progress": {
139
+ "types": "./src/progress.tsx",
140
+ "default": "./dist/progress.js",
141
+ "import": "./dist/progress.js"
142
+ },
143
+ "./radio-group": {
144
+ "types": "./src/radio-group.tsx",
145
+ "default": "./dist/radio-group.js",
146
+ "import": "./dist/radio-group.js"
147
+ },
148
+ "./resizable": {
149
+ "types": "./src/resizable.tsx",
150
+ "default": "./dist/resizable.js",
151
+ "import": "./dist/resizable.js"
152
+ },
153
+ "./select": {
154
+ "types": "./src/select.tsx",
155
+ "default": "./dist/select.js",
156
+ "import": "./dist/select.js"
157
+ },
158
+ "./sidebar": {
159
+ "types": "./src/sidebar.tsx",
160
+ "default": "./dist/sidebar.js",
161
+ "import": "./dist/sidebar.js"
162
+ },
163
+ "./slider": {
164
+ "types": "./src/slider.tsx",
165
+ "default": "./dist/slider.js",
166
+ "import": "./dist/slider.js"
167
+ },
168
+ "./switch": {
169
+ "types": "./src/switch.tsx",
170
+ "default": "./dist/switch.js",
171
+ "import": "./dist/switch.js"
172
+ },
173
+ "./tabs": {
174
+ "types": "./src/tabs.tsx",
175
+ "default": "./dist/tabs.js",
176
+ "import": "./dist/tabs.js"
177
+ },
178
+ "./toast": {
179
+ "types": "./src/toast.tsx",
180
+ "default": "./dist/toast.js",
181
+ "import": "./dist/toast.js"
182
+ },
183
+ "./toggle": {
184
+ "types": "./src/toggle.tsx",
185
+ "default": "./dist/toggle.js",
186
+ "import": "./dist/toggle.js"
187
+ },
188
+ "./toggle-group": {
189
+ "types": "./src/toggle-group.tsx",
190
+ "default": "./dist/toggle-group.js",
191
+ "import": "./dist/toggle-group.js"
192
+ },
193
+ "./toolbar": {
194
+ "types": "./src/toolbar.tsx",
195
+ "default": "./dist/toolbar.js",
196
+ "import": "./dist/toolbar.js"
197
+ },
198
+ "./tooltip": {
199
+ "types": "./src/tooltip.tsx",
200
+ "default": "./dist/tooltip.js",
201
+ "import": "./dist/tooltip.js"
202
+ },
203
+ "./virtual-list": {
204
+ "types": "./src/virtual-list.tsx",
205
+ "default": "./dist/virtual-list.js",
206
+ "import": "./dist/virtual-list.js"
207
+ }
208
+ },
209
+ "dependencies": {
210
+ "@floating-ui/dom": "1.8.0",
211
+ "@tanstack/table-core": "9.0.0-beta.47",
212
+ "@tanstack/virtual-core": "3.17.4",
213
+ "ajo-cloves": "^0.1.0"
214
+ },
215
+ "peerDependencies": {
216
+ "ajo": "^0.1.35"
217
+ },
218
+ "devDependencies": {
219
+ "ajo": "0.1.35"
220
+ },
221
+ "engines": {
222
+ "node": ">=22.18.0"
223
+ },
224
+ "keywords": [
225
+ "ajo",
226
+ "ui",
227
+ "components",
228
+ "accessibility",
229
+ "headless"
230
+ ],
231
+ "author": "Cristian Falcone",
232
+ "license": "ISC",
233
+ "repository": {
234
+ "type": "git",
235
+ "url": "git+https://github.com/cristianfalcone/ajo-kit.git",
236
+ "directory": "packages/ajo-ui"
237
+ },
238
+ "bugs": {
239
+ "url": "https://github.com/cristianfalcone/ajo-kit/issues"
240
+ },
241
+ "homepage": "https://github.com/cristianfalcone/ajo-kit/tree/main/packages/ajo-ui#readme",
242
+ "publishConfig": {
243
+ "access": "public"
244
+ },
245
+ "scripts": {
246
+ "build": "pnpm -w exec tsx scripts/package-build.ts ajo-ui",
247
+ "test": "pnpm -w exec vitest run packages/ajo-ui/tests",
248
+ "test:bundle": "pnpm -w exec tsx packages/ajo-ui/tests/bundle.ts"
249
+ }
250
+ }
@@ -0,0 +1,261 @@
1
+ import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
2
+ import { controlled, listen, roving, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
3
+ import { context } from 'ajo/context'
4
+ import type { FixedArgs, OmitArg } from './utils'
5
+ import { strings } from './utils'
6
+ import { Collapsible, CollapsibleContent, CollapsibleContext, CollapsibleTrigger } from './collapsible'
7
+
8
+ /** Selection model supported by an Accordion root. */
9
+ export type AccordionType = 'multiple' | 'single'
10
+
11
+ type AccordionSharedArgs = WithChildren<OmitArg<IntrinsicElements['div'], 'onchange'> & {
12
+ /** Disable every accordion item. */
13
+ disabled?: boolean
14
+ }> & FixedArgs<'onchange'>
15
+
16
+ /** Arguments for a single-open Accordion. */
17
+ export type AccordionSingleArgs = AccordionSharedArgs & {
18
+ /** Allow the open item to close. */
19
+ collapsible?: boolean
20
+ /** Initial open item for uncontrolled usage. */
21
+ defaultValue?: string
22
+ /** Single-open accordion mode. */
23
+ type?: 'single'
24
+ /** Controlled open item. */
25
+ value?: string
26
+ /** Called whenever the open item changes. */
27
+ onValueChange?: (value: string, event?: Event) => void
28
+ }
29
+
30
+ /** Arguments for an Accordion that may keep several items open. */
31
+ export type AccordionMultipleArgs = AccordionSharedArgs & {
32
+ /** Initial open items for uncontrolled usage. */
33
+ defaultValue?: string[]
34
+ /** Multiple-open accordion mode. */
35
+ type: 'multiple'
36
+ /** Controlled open items. */
37
+ value?: string[]
38
+ /** Called whenever the open items change. */
39
+ onValueChange?: (value: string[], event?: Event) => void
40
+ }
41
+
42
+ /** Public arguments accepted by the Accordion root. */
43
+ export type AccordionArgs = AccordionMultipleArgs | AccordionSingleArgs
44
+
45
+ /** Arguments for one value-bearing Accordion item. */
46
+ export type AccordionItemArgs = WithChildren<OmitArg<IntrinsicElements['details'], 'open'> & {
47
+ /** Stable item value used by the parent accordion. */
48
+ value: string
49
+ /** Disable this item. */
50
+ disabled?: boolean
51
+ }> & FixedArgs<'open'>
52
+
53
+ /** Arguments for an Accordion item's summary trigger. */
54
+ export type AccordionTriggerArgs = WithChildren<IntrinsicElements['summary']>
55
+
56
+ /** Arguments for an Accordion item's collapsible panel. */
57
+ export type AccordionContentArgs = WithChildren<IntrinsicElements['div'] & {
58
+ /** Classes for an inner content wrapper. */
59
+ innerClass?: string
60
+ }>
61
+
62
+ type AccordionRootArgs = WithChildren<{
63
+ collapsible?: boolean
64
+ defaultValue?: string | string[]
65
+ disabled?: boolean
66
+ onValueChange?: ((value: string, event?: Event) => void) | ((value: string[], event?: Event) => void)
67
+ type: AccordionType
68
+ value?: string | string[]
69
+ }>
70
+
71
+ type AccordionContextValue = {
72
+ collapsible: boolean
73
+ disabled: boolean
74
+ isOpen: (value: string) => boolean
75
+ toggle: (value: string, event?: Event) => void
76
+ type: AccordionType
77
+ }
78
+
79
+ const AccordionContext = context<AccordionContextValue | null>(null)
80
+
81
+ const sameArray = (first: string[], second: string[]) =>
82
+ first.length === second.length && first.every((value, index) => value === second[index])
83
+
84
+ const AccordionRoot: Stateful<AccordionRootArgs> = function* ({ defaultValue, type, value }) {
85
+ let collapsible = false
86
+ let disabled = false
87
+ let onValueChange: AccordionArgs['onValueChange']
88
+ let mode: AccordionType = type
89
+ const multipleState = controlled<string[]>(this, {
90
+ fallback: type === 'multiple' ? strings(value ?? defaultValue) : [],
91
+ onChange: (next, event) => {
92
+ const notify = onValueChange as ((value: string[], event?: Event) => void) | undefined
93
+ notify?.(next, event)
94
+ },
95
+ })
96
+ const singleState = controlled<string>(this, {
97
+ fallback: type === 'single' ? String(value ?? defaultValue ?? '') : '',
98
+ onChange: (next, event) => {
99
+ const notify = onValueChange as ((value: string, event?: Event) => void) | undefined
100
+ notify?.(next, event)
101
+ },
102
+ })
103
+ let multiple = multipleState.value
104
+ let single = singleState.value
105
+
106
+ const isOpen = (itemValue: string) =>
107
+ mode === 'multiple' ? multiple.includes(itemValue) : single === itemValue
108
+
109
+ const setSingle = (next: string, event?: Event) => {
110
+ if (next === single) return
111
+ singleState.set(next, event)
112
+ }
113
+
114
+ const setMultiple = (next: string[], event?: Event) => {
115
+ if (sameArray(next, multiple)) return
116
+ multipleState.set(next, event)
117
+ }
118
+
119
+ const toggle = (itemValue: string, event?: Event) => {
120
+ if (disabled) return
121
+
122
+ if (mode === 'multiple') {
123
+ setMultiple(
124
+ multiple.includes(itemValue)
125
+ ? multiple.filter(value => value !== itemValue)
126
+ : [...multiple, itemValue],
127
+ event,
128
+ )
129
+ return
130
+ }
131
+
132
+ if (single === itemValue) {
133
+ if (collapsible) setSingle('', event)
134
+ return
135
+ }
136
+
137
+ setSingle(itemValue, event)
138
+ }
139
+
140
+ const nav = roving(this, {
141
+ items: () => Array.from(this.querySelectorAll<HTMLElement>('[data-slot="accordion-trigger"]'))
142
+ .filter(trigger => trigger.getAttribute('aria-disabled') !== 'true' && trigger.offsetParent !== null),
143
+ onMove: target => target.focus(),
144
+ })
145
+
146
+ listen(this, 'keydown', (event: KeyboardEvent) => {
147
+ const target = event.target as HTMLElement | null
148
+ if (!target?.matches('[data-slot="accordion-trigger"]')) return
149
+
150
+ nav.handle(event)
151
+ })
152
+
153
+ for (const args of this) {
154
+ mode = args.type ?? 'single'
155
+ collapsible = mode === 'single' && Boolean((args as AccordionSingleArgs).collapsible)
156
+ disabled = Boolean(args.disabled)
157
+ onValueChange = args.onValueChange
158
+ multipleState.sync(mode === 'multiple' && args.value != null ? strings(args.value) : undefined)
159
+ singleState.sync(mode === 'single' && args.value != null ? String(args.value ?? '') : undefined)
160
+ multiple = mode === 'multiple' ? multipleState.value : []
161
+ single = mode === 'single' ? singleState.value : ''
162
+
163
+ AccordionContext({ collapsible, disabled, isOpen, toggle, type: mode })
164
+ yield <>{args.children}</>
165
+ }
166
+ }
167
+
168
+
169
+ /** Unstyled root provider for accordion state. */
170
+ const Accordion: Stateless<AccordionArgs> = ({
171
+ children,
172
+ collapsible,
173
+ defaultValue,
174
+ disabled,
175
+ onValueChange,
176
+ type = 'single',
177
+ value,
178
+ ...attrs
179
+ }) => (
180
+ <AccordionRoot
181
+ {...rootAttrs(attrs)}
182
+ collapsible={collapsible === true}
183
+ defaultValue={defaultValue}
184
+ disabled={Boolean(disabled)}
185
+ onValueChange={onValueChange}
186
+ type={type}
187
+ value={value}
188
+ attr:data-slot="accordion"
189
+ >
190
+ {children}
191
+ </AccordionRoot>
192
+ )
193
+
194
+ /** Unstyled accordion section: a group-controlled collapsible details element. */
195
+ const AccordionItem: Stateless<AccordionItemArgs> = ({
196
+ children,
197
+ disabled,
198
+ open: _open,
199
+ value,
200
+ ...attrs
201
+ }) => {
202
+ const accordion = AccordionContext()
203
+ const itemValue = String(value)
204
+ const opened = Boolean(accordion?.isOpen(itemValue))
205
+ const disabledFlag = Boolean(disabled ?? accordion?.disabled)
206
+
207
+ return (
208
+ <Collapsible
209
+ data-orientation="vertical"
210
+ data-slot="accordion-item"
211
+ data-value={value}
212
+ {...attrs}
213
+ disabled={disabledFlag}
214
+ onOpenChange={(_next: boolean, event?: Event) => accordion?.toggle(itemValue, event)}
215
+ open={opened}
216
+ >
217
+ {children}
218
+ </Collapsible>
219
+ )
220
+ }
221
+
222
+ /** Unstyled accordion heading trigger: a collapsible summary locked by group rules. */
223
+ const AccordionTrigger: Stateless<AccordionTriggerArgs> = ({
224
+ children,
225
+ ...attrs
226
+ }) => {
227
+ const accordion = AccordionContext()
228
+ const item = CollapsibleContext()
229
+ const locked = Boolean(item?.open && accordion?.type === 'single' && !accordion.collapsible)
230
+
231
+ return (
232
+ <CollapsibleTrigger
233
+ data-orientation="vertical"
234
+ data-slot="accordion-trigger"
235
+ {...attrs}
236
+ locked={locked}
237
+ >
238
+ {children}
239
+ </CollapsibleTrigger>
240
+ )
241
+ }
242
+
243
+ /** Unstyled accordion panel content. */
244
+ const AccordionContent: Stateless<AccordionContentArgs> = ({
245
+ children,
246
+ innerClass,
247
+ ...attrs
248
+ }) => (
249
+ <CollapsibleContent
250
+ data-orientation="vertical"
251
+ data-slot="accordion-content"
252
+ role="region"
253
+ {...attrs}
254
+ >
255
+ <div class={innerClass}>
256
+ {children}
257
+ </div>
258
+ </CollapsibleContent>
259
+ )
260
+
261
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }