@remix-run/ui 0.1.1 → 0.2.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.
- package/README.md +33 -10
- package/dist/animation/animate-layout-mixin.js +66 -66
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/spring.d.ts +6 -35
- package/dist/animation/spring.js +9 -8
- package/dist/animation/spring.js.map +1 -1
- package/dist/components/accordion/accordion.d.ts +7 -7
- package/dist/components/accordion/accordion.js +0 -2
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/accordion/demos/card.demo.d.ts +6 -0
- package/dist/components/accordion/demos/card.demo.js +58 -0
- package/dist/components/accordion/demos/card.demo.js.map +1 -0
- package/dist/components/accordion/demos/multiple.demo.d.ts +6 -0
- package/dist/components/accordion/demos/multiple.demo.js +19 -0
- package/dist/components/accordion/demos/multiple.demo.js.map +1 -0
- package/dist/components/accordion/demos/overview.demo.d.ts +6 -0
- package/dist/components/accordion/demos/overview.demo.js +19 -0
- package/dist/components/accordion/demos/overview.demo.js.map +1 -0
- package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
- package/dist/components/breadcrumbs/breadcrumbs.js +0 -2
- package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/components/breadcrumbs/demos/basic.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js.map +1 -0
- package/dist/components/breadcrumbs/demos/separator.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js.map +1 -0
- package/dist/components/button/button.d.ts +86 -4
- package/dist/components/button/button.js +63 -3
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/demos/aliases.demo.d.ts +6 -0
- package/dist/components/button/demos/aliases.demo.js +18 -0
- package/dist/components/button/demos/aliases.demo.js.map +1 -0
- package/dist/components/button/demos/basic.demo.d.ts +6 -0
- package/dist/components/button/demos/basic.demo.js +19 -0
- package/dist/components/button/demos/basic.demo.js.map +1 -0
- package/dist/components/button/demos/states.demo.d.ts +6 -0
- package/dist/components/button/demos/states.demo.js +29 -0
- package/dist/components/button/demos/states.demo.js.map +1 -0
- package/dist/components/combobox/combobox.d.ts +11 -11
- package/dist/components/combobox/combobox.js +0 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/combobox/demos/overview.demo.d.ts +6 -0
- package/dist/components/combobox/demos/overview.demo.js +153 -0
- package/dist/components/combobox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/demos/overview.demo.d.ts +6 -0
- package/dist/components/listbox/demos/overview.demo.js +64 -0
- package/dist/components/listbox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/listbox.d.ts +4 -9
- package/dist/components/listbox/listbox.js +21 -33
- package/dist/components/listbox/listbox.js.map +1 -1
- package/dist/components/menu/demos/bubbling.demo.d.ts +5 -0
- package/dist/components/menu/demos/bubbling.demo.js +15 -0
- package/dist/components/menu/demos/bubbling.demo.js.map +1 -0
- package/dist/components/menu/demos/overview.demo.d.ts +6 -0
- package/dist/components/menu/demos/overview.demo.js +32 -0
- package/dist/components/menu/demos/overview.demo.js.map +1 -0
- package/dist/components/menu/menu.d.ts +15 -24
- package/dist/components/menu/menu.js +23 -38
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/popover/demos/overview.demo.d.ts +6 -0
- package/dist/components/popover/demos/overview.demo.js +64 -0
- package/dist/components/popover/demos/overview.demo.js.map +1 -0
- package/dist/components/popover/popover.d.ts +5 -5
- package/dist/components/popover/popover.js.map +1 -1
- package/dist/components/select/demos/deconstructed.demo.d.ts +6 -0
- package/dist/components/select/demos/deconstructed.demo.js +53 -0
- package/dist/components/select/demos/deconstructed.demo.js.map +1 -0
- package/dist/components/select/demos/overview.demo.d.ts +5 -0
- package/dist/components/select/demos/overview.demo.js +27 -0
- package/dist/components/select/demos/overview.demo.js.map +1 -0
- package/dist/components/select/select.d.ts +11 -11
- package/dist/components/select/select.js +17 -26
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +6 -6
- package/dist/components/tabs/tabs.js +0 -2
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interactions/keydown/keydown.d.ts +1 -1
- package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
- package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
- package/dist/runtime/component.d.ts +18 -12
- package/dist/runtime/component.js +93 -80
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.d.ts +14 -0
- package/dist/runtime/core/attributes.js +103 -0
- package/dist/runtime/core/attributes.js.map +1 -0
- package/dist/runtime/core/children.d.ts +7 -0
- package/dist/runtime/core/children.js +30 -0
- package/dist/runtime/core/children.js.map +1 -0
- package/dist/runtime/core/props.d.ts +9 -0
- package/dist/runtime/core/props.js +170 -0
- package/dist/runtime/core/props.js.map +1 -0
- package/dist/runtime/core/vnode.d.ts +3 -0
- package/dist/runtime/core/vnode.js +39 -0
- package/dist/runtime/core/vnode.js.map +1 -0
- package/dist/runtime/create-element.js +2 -1
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +47 -14
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.d.ts +1 -2
- package/dist/runtime/diff-props.js +2 -181
- package/dist/runtime/diff-props.js.map +1 -1
- package/dist/runtime/dom.d.ts +20 -9
- package/dist/runtime/frame.d.ts +23 -2
- package/dist/runtime/frame.js +41 -47
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.d.ts +1 -1
- package/dist/runtime/jsx.js +2 -28
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/mixins/link-mixin.d.ts +5 -1
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +2 -1
- package/dist/runtime/mixins/mixin.js +9 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.d.ts +15 -4
- package/dist/runtime/mixins/on-mixin.js +9 -2
- package/dist/runtime/mixins/on-mixin.js.map +1 -1
- package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
- package/dist/runtime/mixins/ref-mixin.js.map +1 -1
- package/dist/runtime/navigation.js +9 -1
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +1 -1
- package/dist/runtime/reconcile.js +452 -197
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/run.d.ts +17 -2
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.d.ts +10 -10
- package/dist/runtime/scheduler.js +1 -1
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/stream-protocol.d.ts +8 -0
- package/dist/runtime/stream-protocol.js +20 -0
- package/dist/runtime/stream-protocol.js.map +1 -0
- package/dist/runtime/svg-attributes.js +1 -0
- package/dist/runtime/svg-attributes.js.map +1 -1
- package/dist/runtime/to-vnode.js +7 -11
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vnode.d.ts +11 -5
- package/dist/runtime/vnode.js +6 -3
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/server/stream.d.ts +2 -0
- package/dist/server/stream.js +154 -114
- package/dist/server/stream.js.map +1 -1
- package/dist/style/css-mixin.d.ts +2 -1
- package/dist/style/css-mixin.js.map +1 -1
- package/dist/style/index.d.ts +1 -1
- package/dist/style/layers.d.ts +2 -0
- package/dist/style/layers.js +3 -0
- package/dist/style/layers.js.map +1 -0
- package/dist/style/stylesheet.d.ts +13 -11
- package/dist/style/stylesheet.js +69 -8
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/theme/contract.d.ts +24 -222
- package/dist/theme/contract.js +2 -2
- package/dist/theme/contract.js.map +1 -1
- package/dist/theme/glyph-contract.d.ts +1 -3
- package/dist/theme/glyph-contract.js.map +1 -1
- package/dist/theme/presets/rmx-01/index.d.ts +4 -2
- package/dist/theme/presets/rmx-01/index.js.map +1 -1
- package/dist/theme/runtime.js +4 -1
- package/dist/theme/runtime.js.map +1 -1
- package/dist/utils/scroll-lock.d.ts +3 -3
- package/dist/utils/scroll-lock.js.map +1 -1
- package/package.json +3 -3
- package/src/animation/README.md +316 -0
- package/src/animation/animate-layout-mixin.ts +79 -71
- package/src/animation/spring.ts +21 -9
- package/src/components/accordion/README.md +166 -0
- package/src/components/accordion/accordion.tsx +14 -14
- package/src/components/accordion/demos/card.demo.tsx +113 -0
- package/src/components/accordion/demos/multiple.demo.tsx +56 -0
- package/src/components/accordion/demos/overview.demo.tsx +56 -0
- package/src/components/anchor/README.md +153 -0
- package/src/components/anchor/anchor.ts +1 -1
- package/src/components/breadcrumbs/README.md +55 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +1 -3
- package/src/components/breadcrumbs/demos/basic.demo.tsx +17 -0
- package/src/components/breadcrumbs/demos/separator.demo.tsx +18 -0
- package/src/components/button/README.md +4 -4
- package/src/components/button/button.tsx +118 -20
- package/src/components/button/demos/aliases.demo.tsx +27 -0
- package/src/components/button/demos/basic.demo.tsx +30 -0
- package/src/components/button/demos/states.demo.tsx +52 -0
- package/src/components/combobox/README.md +1 -1
- package/src/components/combobox/combobox.tsx +37 -21
- package/src/components/combobox/demos/overview.demo.tsx +199 -0
- package/src/components/glyph/README.md +72 -0
- package/src/components/glyph/glyph.tsx +2 -2
- package/src/components/listbox/README.md +115 -0
- package/src/components/listbox/demos/overview.demo.tsx +101 -0
- package/src/components/listbox/listbox.ts +106 -110
- package/src/components/menu/README.md +96 -0
- package/src/components/menu/demos/bubbling.demo.tsx +34 -0
- package/src/components/menu/demos/overview.demo.tsx +76 -0
- package/src/components/menu/menu.tsx +61 -55
- package/src/components/popover/README.md +3 -3
- package/src/components/popover/demos/overview.demo.tsx +143 -0
- package/src/components/popover/popover.ts +90 -79
- package/src/components/select/README.md +107 -0
- package/src/components/select/demos/deconstructed.demo.tsx +95 -0
- package/src/components/select/demos/overview.demo.tsx +51 -0
- package/src/components/select/select.tsx +53 -36
- package/src/components/tabs/tabs.tsx +10 -5
- package/src/index.ts +2 -1
- package/src/runtime/component.ts +122 -88
- package/src/runtime/core/attributes.ts +118 -0
- package/src/runtime/core/children.ts +42 -0
- package/src/runtime/core/props.ts +187 -0
- package/src/runtime/core/vnode.ts +48 -0
- package/src/runtime/create-element.ts +2 -1
- package/src/runtime/diff-dom.ts +59 -12
- package/src/runtime/diff-props.ts +2 -187
- package/src/runtime/dom.ts +20 -9
- package/src/runtime/frame.ts +71 -55
- package/src/runtime/jsx.ts +3 -30
- package/src/runtime/mixins/link-mixin.ts +88 -86
- package/src/runtime/mixins/mixin.ts +18 -5
- package/src/runtime/mixins/on-mixin.ts +20 -12
- package/src/runtime/mixins/ref-mixin.ts +4 -5
- package/src/runtime/navigation.ts +10 -1
- package/src/runtime/reconcile.ts +563 -223
- package/src/runtime/run.ts +17 -1
- package/src/runtime/scheduler.ts +20 -20
- package/src/runtime/stream-protocol.ts +27 -0
- package/src/runtime/svg-attributes.ts +1 -0
- package/src/runtime/to-vnode.ts +7 -10
- package/src/runtime/vnode.ts +16 -9
- package/src/server/README.md +90 -0
- package/src/server/stream.ts +176 -124
- package/src/style/css-mixin.ts +6 -1
- package/src/style/index.ts +1 -2
- package/src/style/layers.ts +2 -0
- package/src/style/stylesheet.ts +88 -11
- package/src/test/README.md +107 -0
- package/src/test/utils.ts +3 -1
- package/src/theme/README.md +103 -0
- package/src/theme/contract.ts +37 -5
- package/src/theme/glyph-contract.ts +3 -1
- package/src/theme/presets/rmx-01/index.ts +4 -2
- package/src/theme/runtime.ts +4 -1
- package/src/utils/scroll-lock/README.md +33 -0
- package/src/utils/scroll-lock.ts +7 -3
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @jsxRuntime classic
|
|
2
|
-
// @jsx createElement
|
|
3
1
|
import {
|
|
4
2
|
attrs,
|
|
5
3
|
createElement,
|
|
@@ -8,9 +6,11 @@ import {
|
|
|
8
6
|
on,
|
|
9
7
|
ref,
|
|
10
8
|
type CSSMixinDescriptor,
|
|
9
|
+
type Dispatched,
|
|
11
10
|
type ElementProps,
|
|
12
11
|
type Handle,
|
|
13
12
|
type MixinHandle,
|
|
13
|
+
type MixinFactory,
|
|
14
14
|
type Props,
|
|
15
15
|
type RemixNode,
|
|
16
16
|
} from '@remix-run/ui'
|
|
@@ -28,7 +28,10 @@ import type { AnchorOptions } from '../anchor/anchor.ts'
|
|
|
28
28
|
const SELECT_CHANGE_EVENT = 'rmx:select-change' as const
|
|
29
29
|
const LABEL_SWAP_DELAY_MS = 75
|
|
30
30
|
|
|
31
|
-
type SelectChangeHandler = (
|
|
31
|
+
type SelectChangeHandler<target extends HTMLElement> = (
|
|
32
|
+
event: Dispatched<SelectChangeEvent, target>,
|
|
33
|
+
signal: AbortSignal,
|
|
34
|
+
) => void | Promise<void>
|
|
32
35
|
|
|
33
36
|
declare global {
|
|
34
37
|
interface HTMLElementEventMap {
|
|
@@ -74,12 +77,7 @@ export interface SelectProps extends Omit<Props<'button'>, 'children' | 'name'>
|
|
|
74
77
|
|
|
75
78
|
export type SelectOptionProps = Props<'div'> & Omit<listbox.ListboxOption, 'id'>
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
Initializing = 'initializing',
|
|
79
|
-
Closed = 'closed',
|
|
80
|
-
Open = 'open',
|
|
81
|
-
Selecting = 'selecting',
|
|
82
|
-
}
|
|
80
|
+
type State = 'initializing' | 'closed' | 'open' | 'selecting'
|
|
83
81
|
|
|
84
82
|
type PendingChange = {
|
|
85
83
|
label: string | null
|
|
@@ -109,13 +107,13 @@ interface SelectContextValue {
|
|
|
109
107
|
unregisterTrigger: (node: HTMLButtonElement) => void
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>) {
|
|
110
|
+
function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>): () => RemixNode {
|
|
113
111
|
let triggerRef: HTMLButtonElement | undefined
|
|
114
112
|
let listboxRef: listbox.ListboxRef | undefined
|
|
115
113
|
let surfaceRef: HTMLElement | undefined
|
|
116
114
|
let popoverContextRef: popover.PopoverContext | undefined
|
|
117
115
|
|
|
118
|
-
let state: State =
|
|
116
|
+
let state: State = 'initializing'
|
|
119
117
|
|
|
120
118
|
let value: listbox.ListboxValue = null
|
|
121
119
|
let activeValue: listbox.ListboxValue = null
|
|
@@ -128,14 +126,14 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
128
126
|
let listId = `${handle.id}-list`
|
|
129
127
|
|
|
130
128
|
function open() {
|
|
131
|
-
if (state !==
|
|
132
|
-
state =
|
|
129
|
+
if (state !== 'closed' || handle.props.disabled) return
|
|
130
|
+
state = 'open'
|
|
133
131
|
activeValue = value
|
|
134
132
|
handle.update()
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
function syncPopoverMinWidth() {
|
|
138
|
-
if (state !==
|
|
136
|
+
if (state !== 'open' || !surfaceRef || !triggerRef) {
|
|
139
137
|
return
|
|
140
138
|
}
|
|
141
139
|
|
|
@@ -165,8 +163,8 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
165
163
|
}
|
|
166
164
|
|
|
167
165
|
function close() {
|
|
168
|
-
if (state !==
|
|
169
|
-
state =
|
|
166
|
+
if (state !== 'open') return
|
|
167
|
+
state = 'closed'
|
|
170
168
|
handle.update()
|
|
171
169
|
}
|
|
172
170
|
|
|
@@ -215,22 +213,22 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
215
213
|
nextValue: listbox.ListboxValue,
|
|
216
214
|
option: listbox.ListboxOption | undefined,
|
|
217
215
|
) {
|
|
218
|
-
if (state !==
|
|
216
|
+
if (state !== 'open') return
|
|
219
217
|
pendingChange = getPendingChange(nextValue, option)
|
|
220
218
|
setSelectedOption(nextValue, option)
|
|
221
219
|
handle.update()
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
async function settleSelectedOption() {
|
|
225
|
-
if (state !==
|
|
223
|
+
if (state !== 'open') return
|
|
226
224
|
|
|
227
225
|
let change = pendingChange
|
|
228
226
|
pendingChange = null
|
|
229
|
-
state =
|
|
227
|
+
state = 'selecting'
|
|
230
228
|
|
|
231
229
|
if (!surfaceRef) {
|
|
232
230
|
displayedLabel = selectedLabel
|
|
233
|
-
state =
|
|
231
|
+
state = 'closed'
|
|
234
232
|
let signal = await handle.update()
|
|
235
233
|
if (signal.aborted) return
|
|
236
234
|
dispatchChange(change)
|
|
@@ -241,14 +239,14 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
241
239
|
await wait(LABEL_SWAP_DELAY_MS) // UX delay label swap for clear value change
|
|
242
240
|
if (handle.signal.aborted) return
|
|
243
241
|
displayedLabel = selectedLabel
|
|
244
|
-
state =
|
|
242
|
+
state = 'closed'
|
|
245
243
|
let signal = await handle.update()
|
|
246
244
|
if (signal.aborted) return
|
|
247
245
|
dispatchChange(change)
|
|
248
246
|
}
|
|
249
247
|
|
|
250
248
|
function selectTypeaheadMatch(text: string) {
|
|
251
|
-
if (state !==
|
|
249
|
+
if (state !== 'closed' || handle.props.disabled) return
|
|
252
250
|
|
|
253
251
|
let option = listboxRef?.matchSearchText(text, value)
|
|
254
252
|
if (!option) return
|
|
@@ -266,7 +264,7 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
266
264
|
nextActiveValue: listbox.ListboxValue,
|
|
267
265
|
option: listbox.ListboxOption | undefined,
|
|
268
266
|
) {
|
|
269
|
-
if (state !==
|
|
267
|
+
if (state !== 'open') return
|
|
270
268
|
activeValue = nextActiveValue
|
|
271
269
|
activeId = option?.id
|
|
272
270
|
handle.update()
|
|
@@ -286,11 +284,11 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
286
284
|
},
|
|
287
285
|
|
|
288
286
|
get isExpanded() {
|
|
289
|
-
return state ===
|
|
287
|
+
return state === 'open' || state === 'selecting'
|
|
290
288
|
},
|
|
291
289
|
|
|
292
290
|
get isOpen() {
|
|
293
|
-
return state ===
|
|
291
|
+
return state === 'open'
|
|
294
292
|
},
|
|
295
293
|
|
|
296
294
|
get listId() {
|
|
@@ -356,11 +354,11 @@ function SelectProvider(handle: Handle<SelectContextProps, SelectContextValue>)
|
|
|
356
354
|
})
|
|
357
355
|
|
|
358
356
|
return () => {
|
|
359
|
-
if (state ===
|
|
357
|
+
if (state === 'initializing') {
|
|
360
358
|
selectedLabel = displayedLabel = handle.props.defaultLabel
|
|
361
359
|
value = handle.props.defaultValue ?? null
|
|
362
360
|
activeValue = value
|
|
363
|
-
state =
|
|
361
|
+
state = 'closed'
|
|
364
362
|
|
|
365
363
|
handle.queueTask(() => {
|
|
366
364
|
if (selectedId || !surfaceRef) {
|
|
@@ -399,7 +397,11 @@ function getSelectContext(handle: Handle | MixinHandle) {
|
|
|
399
397
|
return handle.context.get(SelectProvider)
|
|
400
398
|
}
|
|
401
399
|
|
|
402
|
-
const triggerMixin
|
|
400
|
+
const triggerMixin: MixinFactory<HTMLButtonElement, [], ElementProps> = createMixin<
|
|
401
|
+
HTMLButtonElement,
|
|
402
|
+
[],
|
|
403
|
+
ElementProps
|
|
404
|
+
>((handle) => {
|
|
403
405
|
let context = getSelectContext(handle)
|
|
404
406
|
|
|
405
407
|
return (props) => [
|
|
@@ -431,7 +433,11 @@ const triggerMixin = createMixin<HTMLButtonElement, [], ElementProps>((handle) =
|
|
|
431
433
|
]
|
|
432
434
|
})
|
|
433
435
|
|
|
434
|
-
const popoverMixin
|
|
436
|
+
const popoverMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
|
|
437
|
+
HTMLElement,
|
|
438
|
+
[],
|
|
439
|
+
ElementProps
|
|
440
|
+
>((handle) => {
|
|
435
441
|
let context = getSelectContext(handle)
|
|
436
442
|
let popoverState = handle.context.get(popover.Context)
|
|
437
443
|
|
|
@@ -456,7 +462,11 @@ const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
|
|
|
456
462
|
]
|
|
457
463
|
})
|
|
458
464
|
|
|
459
|
-
const listMixin
|
|
465
|
+
const listMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
|
|
466
|
+
HTMLElement,
|
|
467
|
+
[],
|
|
468
|
+
ElementProps
|
|
469
|
+
>((handle) => {
|
|
460
470
|
let context = getSelectContext(handle)
|
|
461
471
|
|
|
462
472
|
return () => [
|
|
@@ -469,7 +479,11 @@ const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
|
|
|
469
479
|
]
|
|
470
480
|
})
|
|
471
481
|
|
|
472
|
-
const hiddenInputMixin
|
|
482
|
+
const hiddenInputMixin: MixinFactory<HTMLInputElement, [], ElementProps> = createMixin<
|
|
483
|
+
HTMLInputElement,
|
|
484
|
+
[],
|
|
485
|
+
ElementProps
|
|
486
|
+
>((handle) => {
|
|
473
487
|
let context = getSelectContext(handle)
|
|
474
488
|
|
|
475
489
|
return () =>
|
|
@@ -532,17 +546,20 @@ const select = {
|
|
|
532
546
|
trigger,
|
|
533
547
|
} as const
|
|
534
548
|
|
|
535
|
-
export function onSelectChange
|
|
536
|
-
|
|
549
|
+
export function onSelectChange<target extends HTMLElement>(
|
|
550
|
+
handler: SelectChangeHandler<target>,
|
|
551
|
+
captureBoolean?: boolean,
|
|
552
|
+
): ReturnType<typeof on<target, typeof SELECT_CHANGE_EVENT>> {
|
|
553
|
+
return on(SELECT_CHANGE_EVENT, handler, captureBoolean)
|
|
537
554
|
}
|
|
538
555
|
|
|
539
|
-
function SelectLabel(handle: Handle) {
|
|
556
|
+
function SelectLabel(handle: Handle): () => RemixNode {
|
|
540
557
|
let context = getSelectContext(handle)
|
|
541
558
|
|
|
542
559
|
return () => <span mix={button.labelStyle}>{context.displayedLabel}</span>
|
|
543
560
|
}
|
|
544
561
|
|
|
545
|
-
export function Select(handle: Handle<SelectProps>) {
|
|
562
|
+
export function Select(handle: Handle<SelectProps>): () => RemixNode {
|
|
546
563
|
return () => {
|
|
547
564
|
let { children, defaultLabel, defaultValue, disabled, name, mix, ...buttonProps } = handle.props
|
|
548
565
|
|
|
@@ -568,7 +585,7 @@ export function Select(handle: Handle<SelectProps>) {
|
|
|
568
585
|
}
|
|
569
586
|
}
|
|
570
587
|
|
|
571
|
-
export function Option(handle: Handle<SelectOptionProps>) {
|
|
588
|
+
export function Option(handle: Handle<SelectOptionProps>): () => RemixNode {
|
|
572
589
|
return () => {
|
|
573
590
|
let { label, value, disabled, textValue, children, mix, ...divProps } = handle.props
|
|
574
591
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @jsxRuntime classic
|
|
2
|
-
// @jsx createElement
|
|
3
1
|
import {
|
|
4
2
|
attrs,
|
|
5
3
|
css,
|
|
@@ -7,6 +5,7 @@ import {
|
|
|
7
5
|
createMixin,
|
|
8
6
|
on,
|
|
9
7
|
type CSSMixinDescriptor,
|
|
8
|
+
type Dispatched,
|
|
10
9
|
type ElementProps,
|
|
11
10
|
type Handle,
|
|
12
11
|
type MixinHandle,
|
|
@@ -18,7 +17,10 @@ import { theme } from '../../theme/theme.ts'
|
|
|
18
17
|
|
|
19
18
|
const TABS_CHANGE_EVENT = 'rmx:tabs-change' as const
|
|
20
19
|
|
|
21
|
-
type TabsChangeHandler = (
|
|
20
|
+
type TabsChangeHandler<target extends HTMLElement> = (
|
|
21
|
+
event: Dispatched<TabsChangeEvent, target>,
|
|
22
|
+
signal: AbortSignal,
|
|
23
|
+
) => void | Promise<void>
|
|
22
24
|
|
|
23
25
|
const tabsListCss: CSSMixinDescriptor = css({
|
|
24
26
|
display: 'inline-flex',
|
|
@@ -446,8 +448,11 @@ export const trigger = triggerMixin
|
|
|
446
448
|
|
|
447
449
|
const tabs = { Context, list, panel, trigger } as const
|
|
448
450
|
|
|
449
|
-
export function onTabsChange
|
|
450
|
-
|
|
451
|
+
export function onTabsChange<target extends HTMLElement>(
|
|
452
|
+
handler: TabsChangeHandler<target>,
|
|
453
|
+
captureBoolean?: boolean,
|
|
454
|
+
) {
|
|
455
|
+
return on(TABS_CHANGE_EVENT, handler, captureBoolean)
|
|
451
456
|
}
|
|
452
457
|
|
|
453
458
|
export function Tabs(handle: Handle<TabsProps>) {
|
package/src/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type { HostProps, LayoutAnimationConfig } from './runtime/dom.ts'
|
|
|
51
51
|
export { createMixin } from './runtime/mixins/mixin.ts'
|
|
52
52
|
export type {
|
|
53
53
|
MixinDescriptor,
|
|
54
|
+
MixinFactory,
|
|
54
55
|
MixinHandle,
|
|
55
56
|
MixinType,
|
|
56
57
|
MixInput,
|
|
@@ -58,8 +59,8 @@ export type {
|
|
|
58
59
|
} from './runtime/mixins/mixin.ts'
|
|
59
60
|
export { TypedEventTarget } from './runtime/typed-event-target.ts'
|
|
60
61
|
export { addEventListeners } from './runtime/event-listeners.ts'
|
|
62
|
+
export type { Dispatched } from './runtime/event-listeners.ts'
|
|
61
63
|
export { on } from './runtime/mixins/on-mixin.ts'
|
|
62
|
-
export type { Dispatched } from './runtime/mixins/on-mixin.ts'
|
|
63
64
|
export { link } from './runtime/mixins/link-mixin.ts'
|
|
64
65
|
export { ref } from './runtime/mixins/ref-mixin.ts'
|
|
65
66
|
export type { RefCallback } from './runtime/mixins/ref-mixin.ts'
|
package/src/runtime/component.ts
CHANGED
|
@@ -118,13 +118,18 @@ export type ContextFrom<ComponentType> =
|
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Context storage API exposed on component handles.
|
|
121
|
+
*
|
|
122
|
+
* Context values are keyed by provider component identity. `get(Component)`
|
|
123
|
+
* reads the nearest ancestor instance whose component function is exactly
|
|
124
|
+
* `Component`, so nested instances of the same provider shadow outer instances
|
|
125
|
+
* while different component types remain independent.
|
|
121
126
|
*/
|
|
122
127
|
export interface Context<C> {
|
|
123
128
|
/** Replaces the current context value for this component instance. */
|
|
124
129
|
set(values: C): void
|
|
125
|
-
/** Reads the context value
|
|
130
|
+
/** Reads the context value from the nearest ancestor instance of the given component type. */
|
|
126
131
|
get<ComponentType>(component: ComponentType): ContextFrom<ComponentType>
|
|
127
|
-
/** Reads
|
|
132
|
+
/** Reads an unknown context value for an untyped lookup. */
|
|
128
133
|
get(component: ElementType | symbol): unknown | undefined
|
|
129
134
|
}
|
|
130
135
|
|
|
@@ -218,7 +223,14 @@ type ComponentConfig = {
|
|
|
218
223
|
/**
|
|
219
224
|
* Runtime handle returned by {@link createComponent}.
|
|
220
225
|
*/
|
|
221
|
-
export
|
|
226
|
+
export interface ComponentHandle<C = NoContext> {
|
|
227
|
+
frame: FrameHandle
|
|
228
|
+
render(nextProps: ElementProps): [RemixNode, Array<() => void>]
|
|
229
|
+
remove(): Array<() => void>
|
|
230
|
+
setScheduleUpdate(nextScheduleUpdate: () => void): void
|
|
231
|
+
getContextValue(): C | undefined
|
|
232
|
+
isRemoved(): boolean
|
|
233
|
+
}
|
|
222
234
|
|
|
223
235
|
/**
|
|
224
236
|
* Creates the internal runtime wrapper for a component instance.
|
|
@@ -226,114 +238,136 @@ export type ComponentHandle = ReturnType<typeof createComponent>
|
|
|
226
238
|
* @param config Component runtime configuration.
|
|
227
239
|
* @returns Component runtime helpers used by the reconciler.
|
|
228
240
|
*/
|
|
229
|
-
export function createComponent<C = NoContext>(config: ComponentConfig) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
let connectedCtrl: AbortController | null = null
|
|
233
|
-
let contextValue: C | undefined = undefined
|
|
234
|
-
|
|
235
|
-
function getConnectedSignal() {
|
|
236
|
-
if (!connectedCtrl) connectedCtrl = new AbortController()
|
|
237
|
-
return connectedCtrl.signal
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let getContent: null | RenderFn = null
|
|
241
|
-
let scheduleUpdate: () => void = () => {
|
|
242
|
-
throw new Error('scheduleUpdate not implemented')
|
|
243
|
-
}
|
|
244
|
-
let props = {} as ElementProps
|
|
241
|
+
export function createComponent<C = NoContext>(config: ComponentConfig): ComponentHandle<C> {
|
|
242
|
+
return new ComponentRuntime<C>(config)
|
|
243
|
+
}
|
|
245
244
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
contextValue = value
|
|
249
|
-
},
|
|
250
|
-
get: (type: Component) => config.getContext(type),
|
|
251
|
-
}
|
|
245
|
+
class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
246
|
+
frame: FrameHandle
|
|
252
247
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
},
|
|
264
|
-
frame: config.frame,
|
|
265
|
-
frames: {
|
|
266
|
-
get top() {
|
|
267
|
-
return config.getTopFrame?.() ?? config.frame
|
|
268
|
-
},
|
|
269
|
-
get(name: string) {
|
|
270
|
-
return config.getFrameByName(name)
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
context: context,
|
|
274
|
-
get signal() {
|
|
275
|
-
return config.signal ?? getConnectedSignal()
|
|
276
|
-
},
|
|
248
|
+
#config: ComponentConfig
|
|
249
|
+
#connectedController: AbortController | undefined
|
|
250
|
+
#contextValue: C | undefined
|
|
251
|
+
#handle: Handle<ElementProps, C>
|
|
252
|
+
#props = {} as ElementProps
|
|
253
|
+
#renderController: AbortController | undefined
|
|
254
|
+
#renderFn: RenderFn | undefined
|
|
255
|
+
#removed = false
|
|
256
|
+
#scheduleUpdate: () => void = () => {
|
|
257
|
+
throw new Error('scheduleUpdate not implemented')
|
|
277
258
|
}
|
|
259
|
+
#tasks: Task[] = []
|
|
278
260
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
renderCtrl = new AbortController()
|
|
284
|
-
}
|
|
285
|
-
let signal = renderCtrl?.signal
|
|
286
|
-
return taskQueue.splice(0, taskQueue.length).map((task) => () => task(signal!))
|
|
261
|
+
constructor(config: ComponentConfig) {
|
|
262
|
+
this.#config = config
|
|
263
|
+
this.frame = config.frame
|
|
264
|
+
this.#handle = this.#createHandle()
|
|
287
265
|
}
|
|
288
266
|
|
|
289
|
-
|
|
290
|
-
if (
|
|
267
|
+
render = (nextProps: ElementProps): [RemixNode, Array<() => void>] => {
|
|
268
|
+
if (this.#removed) {
|
|
291
269
|
console.warn('render called after component was removed, potential application memory leak')
|
|
292
270
|
return [null, []]
|
|
293
271
|
}
|
|
294
272
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
273
|
+
this.#abortRenderSignal()
|
|
274
|
+
syncProps(this.#props, nextProps)
|
|
275
|
+
|
|
276
|
+
let renderFn = this.#renderFn
|
|
300
277
|
|
|
301
|
-
|
|
278
|
+
if (renderFn === undefined) {
|
|
279
|
+
let result = this.#config.type(this.#handle)
|
|
302
280
|
|
|
303
|
-
let renderContent = getContent
|
|
304
|
-
if (!renderContent) {
|
|
305
|
-
let result = config.type(handle)
|
|
306
281
|
if (typeof result !== 'function') {
|
|
307
|
-
let name = config.type.name || 'Anonymous'
|
|
282
|
+
let name = this.#config.type.name || 'Anonymous'
|
|
308
283
|
throw new Error(`${name} must return a render function, received ${typeof result}`)
|
|
309
|
-
} else {
|
|
310
|
-
getContent = result
|
|
311
|
-
renderContent = result
|
|
312
284
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
285
|
+
|
|
286
|
+
renderFn = result as RenderFn
|
|
287
|
+
this.#renderFn = renderFn
|
|
316
288
|
}
|
|
317
289
|
|
|
318
|
-
|
|
319
|
-
return [node, dequeueTasks()]
|
|
290
|
+
return [renderFn(this.#props), this.#dequeueTasks()]
|
|
320
291
|
}
|
|
321
292
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
293
|
+
remove = (): Array<() => void> => {
|
|
294
|
+
if (this.#removed) return []
|
|
295
|
+
this.#removed = true
|
|
296
|
+
this.#connectedController?.abort()
|
|
297
|
+
this.#abortRenderSignal()
|
|
298
|
+
return this.#dequeueTasks(AbortSignal.abort())
|
|
326
299
|
}
|
|
327
300
|
|
|
328
|
-
|
|
329
|
-
scheduleUpdate = nextScheduleUpdate
|
|
301
|
+
setScheduleUpdate = (nextScheduleUpdate: () => void): void => {
|
|
302
|
+
this.#scheduleUpdate = nextScheduleUpdate
|
|
330
303
|
}
|
|
331
304
|
|
|
332
|
-
|
|
333
|
-
|
|
305
|
+
getContextValue = (): C | undefined => this.#contextValue
|
|
306
|
+
|
|
307
|
+
isRemoved = (): boolean => this.#removed
|
|
308
|
+
|
|
309
|
+
#createHandle(): Handle<ElementProps, C> {
|
|
310
|
+
let component = this
|
|
311
|
+
let context: Context<C> = {
|
|
312
|
+
set: (value: C) => {
|
|
313
|
+
this.#contextValue = value
|
|
314
|
+
},
|
|
315
|
+
get: (type: ElementType | symbol) => this.#config.getContext(type as Component),
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
id: this.#config.id,
|
|
320
|
+
props: this.#props,
|
|
321
|
+
update: () =>
|
|
322
|
+
new Promise((resolve) => {
|
|
323
|
+
if (component.#removed) {
|
|
324
|
+
resolve(AbortSignal.abort())
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
this.#tasks.push((signal) => resolve(signal))
|
|
329
|
+
this.#scheduleUpdate()
|
|
330
|
+
}),
|
|
331
|
+
queueTask: (task: Task) => {
|
|
332
|
+
this.#tasks.push(task)
|
|
333
|
+
},
|
|
334
|
+
frame: this.#config.frame,
|
|
335
|
+
frames: {
|
|
336
|
+
get top() {
|
|
337
|
+
return component.#config.getTopFrame?.() ?? component.#config.frame
|
|
338
|
+
},
|
|
339
|
+
get(name: string) {
|
|
340
|
+
return component.#config.getFrameByName(name)
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
context,
|
|
344
|
+
get signal() {
|
|
345
|
+
return component.#config.signal ?? component.#connectedSignal()
|
|
346
|
+
},
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
#connectedSignal(): AbortSignal {
|
|
351
|
+
this.#connectedController ??= new AbortController()
|
|
352
|
+
return this.#connectedController.signal
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#abortRenderSignal(): void {
|
|
356
|
+
this.#renderController?.abort()
|
|
357
|
+
this.#renderController = undefined
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
|
|
360
|
+
#dequeueTasks(signal?: AbortSignal): Array<() => void> {
|
|
361
|
+
let needsSignal = signal === undefined && this.#tasks.some((task) => task.length >= 1)
|
|
362
|
+
|
|
363
|
+
if (needsSignal) {
|
|
364
|
+
this.#renderController ??= new AbortController()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
signal ??= this.#renderController?.signal
|
|
368
|
+
let tasks = this.#tasks.splice(0, this.#tasks.length)
|
|
369
|
+
return tasks.map((task) => () => task(signal!))
|
|
370
|
+
}
|
|
337
371
|
}
|
|
338
372
|
|
|
339
373
|
function syncProps(target: ElementProps, next: ElementProps): void {
|
|
@@ -354,7 +388,7 @@ function syncProps(target: ElementProps, next: ElementProps): void {
|
|
|
354
388
|
* @param handle Component handle for the frame instance.
|
|
355
389
|
* @returns A placeholder render function handled by the reconciler.
|
|
356
390
|
*/
|
|
357
|
-
export function Frame(handle: Handle<FrameProps, FrameHandle>) {
|
|
391
|
+
export function Frame(handle: Handle<FrameProps, FrameHandle>): RenderFn {
|
|
358
392
|
void handle
|
|
359
393
|
return () => null // reconciler renders
|
|
360
394
|
}
|
|
@@ -365,7 +399,7 @@ export function Frame(handle: Handle<FrameProps, FrameHandle>) {
|
|
|
365
399
|
* @param handle Component handle for the fragment instance.
|
|
366
400
|
* @returns A placeholder render function handled by the reconciler.
|
|
367
401
|
*/
|
|
368
|
-
export function Fragment(handle: Handle<FragmentProps>) {
|
|
402
|
+
export function Fragment(handle: Handle<FragmentProps>): RenderFn {
|
|
369
403
|
void handle
|
|
370
404
|
return () => null // reconciler renders
|
|
371
405
|
}
|