@remix-run/ui 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -164
- package/dist/accordion/index.d.ts +46 -0
- package/dist/accordion/index.js +142 -0
- package/dist/accordion/index.js.map +1 -0
- package/dist/accordion/primitives.d.ts +104 -0
- package/dist/accordion/primitives.js +312 -0
- package/dist/accordion/primitives.js.map +1 -0
- package/dist/{components/anchor/anchor.js → anchor/index.js} +1 -1
- package/dist/anchor/index.js.map +1 -0
- package/dist/animation/demos/reordering.js +9 -6
- package/dist/animation/demos/reordering.js.map +1 -1
- package/dist/{components/breadcrumbs/breadcrumbs.js → breadcrumbs/index.js} +19 -19
- package/dist/breadcrumbs/index.js.map +1 -0
- package/dist/button/index.d.ts +15 -0
- package/dist/button/index.js +128 -0
- package/dist/button/index.js.map +1 -0
- package/dist/checkbox/index.d.ts +14 -0
- package/dist/checkbox/index.js +125 -0
- package/dist/checkbox/index.js.map +1 -0
- package/dist/combobox/index.d.ts +21 -0
- package/dist/combobox/index.js +58 -0
- package/dist/combobox/index.js.map +1 -0
- package/dist/{components/combobox/combobox.d.ts → combobox/primitives.d.ts} +3 -7
- package/dist/{components/combobox/combobox.js → combobox/primitives.js} +8 -61
- package/dist/combobox/primitives.js.map +1 -0
- package/dist/input/index.d.ts +13 -0
- package/dist/input/index.js +121 -0
- package/dist/input/index.js.map +1 -0
- package/dist/listbox/flash-attribute.js.map +1 -0
- package/dist/{components/listbox/listbox.d.ts → listbox/index.d.ts} +2 -6
- package/dist/{components/listbox/listbox.js → listbox/index.js} +4 -82
- package/dist/listbox/index.js.map +1 -0
- package/dist/menu/hover-aim.js.map +1 -0
- package/dist/menu/index.d.ts +42 -0
- package/dist/menu/index.js +250 -0
- package/dist/menu/index.js.map +1 -0
- package/dist/{components/menu/menu.d.ts → menu/primitives.d.ts} +3 -32
- package/dist/{components/menu/menu.js → menu/primitives.js} +97 -314
- package/dist/menu/primitives.js.map +1 -0
- package/dist/{components/popover/popover.d.ts → popover/index.d.ts} +2 -4
- package/dist/{components/popover/popover.js → popover/index.js} +5 -48
- package/dist/popover/index.js.map +1 -0
- package/dist/{interactions/outside-click/outside-click-mixin.js → popover/outside-click.js} +1 -1
- package/dist/popover/outside-click.js.map +1 -0
- package/dist/popover/scroll-lock.d.ts +1 -0
- package/dist/{utils → popover}/scroll-lock.js +0 -18
- package/dist/popover/scroll-lock.js.map +1 -0
- package/dist/radio/index.d.ts +12 -0
- package/dist/radio/index.js +77 -0
- package/dist/radio/index.js.map +1 -0
- package/dist/runtime/reconcile.js +11 -5
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/vdom.js +3 -0
- package/dist/runtime/vdom.js.map +1 -1
- package/dist/select/index.d.ts +19 -0
- package/dist/select/index.js +95 -0
- package/dist/select/index.js.map +1 -0
- package/dist/select/keydown.js.map +1 -0
- package/dist/{components/select/select.d.ts → select/primitives.d.ts} +4 -6
- package/dist/{components/select/select.js → select/primitives.js} +11 -63
- package/dist/select/primitives.js.map +1 -0
- package/dist/server/stream.js +2 -2
- package/dist/server/stream.js.map +1 -1
- package/dist/shared/focus-styles.d.ts +1 -0
- package/dist/shared/focus-styles.js +2 -0
- package/dist/shared/focus-styles.js.map +1 -0
- package/dist/shared/icons.d.ts +9 -0
- package/dist/shared/icons.js +42 -0
- package/dist/shared/icons.js.map +1 -0
- package/dist/shared/listbox-popover-styles.d.ts +6 -0
- package/dist/shared/listbox-popover-styles.js +137 -0
- package/dist/shared/listbox-popover-styles.js.map +1 -0
- package/dist/shared/style-values.d.ts +78 -0
- package/dist/{theme/presets/rmx-01/index.js → shared/style-values.js} +3 -34
- package/dist/shared/style-values.js.map +1 -0
- package/dist/{interactions/typeahead/typeahead-mixin.js → shared/typeahead.js} +1 -1
- package/dist/shared/typeahead.js.map +1 -0
- package/dist/shared/wait-for-css-transition.js.map +1 -0
- package/dist/shared/wait.js.map +1 -0
- package/dist/tabs/index.d.ts +32 -0
- package/dist/tabs/index.js +157 -0
- package/dist/tabs/index.js.map +1 -0
- package/dist/tabs/primitives.d.ts +57 -0
- package/dist/tabs/primitives.js +238 -0
- package/dist/tabs/primitives.js.map +1 -0
- package/dist/toggle/index.d.ts +9 -0
- package/dist/toggle/index.js +108 -0
- package/dist/toggle/index.js.map +1 -0
- package/dist/toggle/primitives.d.ts +29 -0
- package/dist/toggle/primitives.js +127 -0
- package/dist/toggle/primitives.js.map +1 -0
- package/package.json +73 -44
- package/src/{components/accordion → accordion}/README.md +50 -14
- package/src/accordion/index.tsx +285 -0
- package/src/accordion/primitives.tsx +512 -0
- package/src/{components/anchor → anchor}/README.md +37 -2
- package/src/animation/demos/reordering.tsx +9 -6
- package/src/{components/breadcrumbs → breadcrumbs}/README.md +4 -4
- package/src/{components/breadcrumbs/breadcrumbs.tsx → breadcrumbs/index.tsx} +18 -18
- package/src/button/README.md +44 -0
- package/src/button/index.ts +168 -0
- package/src/checkbox/README.md +59 -0
- package/src/checkbox/index.ts +162 -0
- package/src/{components/combobox → combobox}/README.md +58 -9
- package/src/combobox/index.tsx +112 -0
- package/src/{components/combobox/combobox.tsx → combobox/primitives.tsx} +5 -91
- package/src/input/README.md +52 -0
- package/src/input/index.ts +147 -0
- package/src/{components/listbox → listbox}/README.md +9 -41
- package/src/{components/listbox/listbox.ts → listbox/index.ts} +2 -87
- package/src/{components/menu → menu}/README.md +55 -14
- package/src/menu/index.tsx +354 -0
- package/src/{components/menu/menu.tsx → menu/primitives.tsx} +99 -413
- package/src/{components/popover → popover}/README.md +20 -39
- package/src/{components/popover/popover.ts → popover/index.ts} +3 -57
- package/src/{utils → popover}/scroll-lock.ts +0 -28
- package/src/radio/README.md +53 -0
- package/src/radio/index.ts +100 -0
- package/src/runtime/demos/readme.demo.tsx +16 -14
- package/src/runtime/reconcile.ts +11 -5
- package/src/runtime/vdom.ts +3 -0
- package/src/{components/select → select}/README.md +29 -19
- package/src/select/index.tsx +159 -0
- package/src/{components/select/select.tsx → select/primitives.tsx} +10 -98
- package/src/server/stream.ts +3 -3
- package/src/shared/focus-styles.ts +2 -0
- package/src/shared/icons.tsx +62 -0
- package/src/shared/listbox-popover-styles.ts +146 -0
- package/src/shared/style-values.ts +165 -0
- package/src/tabs/README.md +141 -0
- package/src/tabs/index.tsx +244 -0
- package/src/tabs/primitives.ts +365 -0
- package/src/toggle/README.md +56 -0
- package/src/toggle/index.tsx +134 -0
- package/src/toggle/primitives.ts +192 -0
- package/dist/animation/demos/animation.demo.d.ts +0 -5
- package/dist/animation/demos/animation.demo.js +0 -92
- package/dist/animation/demos/animation.demo.js.map +0 -1
- package/dist/animation/demos/spring.demo.d.ts +0 -6
- package/dist/animation/demos/spring.demo.js +0 -304
- package/dist/animation/demos/spring.demo.js.map +0 -1
- package/dist/components/accordion/accordion.d.ts +0 -92
- package/dist/components/accordion/accordion.js +0 -335
- package/dist/components/accordion/accordion.js.map +0 -1
- package/dist/components/accordion/demos/card.demo.d.ts +0 -7
- package/dist/components/accordion/demos/card.demo.js +0 -59
- package/dist/components/accordion/demos/card.demo.js.map +0 -1
- package/dist/components/accordion/demos/multiple.demo.d.ts +0 -7
- package/dist/components/accordion/demos/multiple.demo.js +0 -20
- package/dist/components/accordion/demos/multiple.demo.js.map +0 -1
- package/dist/components/accordion/demos/overview.demo.d.ts +0 -7
- package/dist/components/accordion/demos/overview.demo.js +0 -20
- package/dist/components/accordion/demos/overview.demo.js.map +0 -1
- package/dist/components/anchor/anchor.js.map +0 -1
- package/dist/components/breadcrumbs/basic.demo.d.ts +0 -6
- package/dist/components/breadcrumbs/basic.demo.js +0 -15
- package/dist/components/breadcrumbs/basic.demo.js.map +0 -1
- package/dist/components/breadcrumbs/breadcrumbs.js.map +0 -1
- package/dist/components/breadcrumbs/separator.demo.d.ts +0 -6
- package/dist/components/breadcrumbs/separator.demo.js +0 -15
- package/dist/components/breadcrumbs/separator.demo.js.map +0 -1
- package/dist/components/button/button.d.ts +0 -105
- package/dist/components/button/button.js +0 -207
- package/dist/components/button/button.js.map +0 -1
- package/dist/components/button/demos/aliases.demo.d.ts +0 -7
- package/dist/components/button/demos/aliases.demo.js +0 -19
- package/dist/components/button/demos/aliases.demo.js.map +0 -1
- package/dist/components/button/demos/basic.demo.d.ts +0 -7
- package/dist/components/button/demos/basic.demo.js +0 -20
- package/dist/components/button/demos/basic.demo.js.map +0 -1
- package/dist/components/button/demos/states.demo.d.ts +0 -7
- package/dist/components/button/demos/states.demo.js +0 -30
- package/dist/components/button/demos/states.demo.js.map +0 -1
- package/dist/components/combobox/combobox.js.map +0 -1
- package/dist/components/combobox/overview.demo.d.ts +0 -7
- package/dist/components/combobox/overview.demo.js +0 -154
- package/dist/components/combobox/overview.demo.js.map +0 -1
- package/dist/components/glyph/glyph.d.ts +0 -14
- package/dist/components/glyph/glyph.js +0 -65
- package/dist/components/glyph/glyph.js.map +0 -1
- package/dist/components/listbox/listbox.js.map +0 -1
- package/dist/components/listbox/overview.demo.d.ts +0 -7
- package/dist/components/listbox/overview.demo.js +0 -65
- package/dist/components/listbox/overview.demo.js.map +0 -1
- package/dist/components/menu/demos/bubbling.demo.d.ts +0 -6
- package/dist/components/menu/demos/bubbling.demo.js +0 -16
- package/dist/components/menu/demos/bubbling.demo.js.map +0 -1
- package/dist/components/menu/demos/context-trigger.demo.d.ts +0 -7
- package/dist/components/menu/demos/context-trigger.demo.js +0 -87
- package/dist/components/menu/demos/context-trigger.demo.js.map +0 -1
- package/dist/components/menu/demos/overview.demo.d.ts +0 -7
- package/dist/components/menu/demos/overview.demo.js +0 -33
- package/dist/components/menu/demos/overview.demo.js.map +0 -1
- package/dist/components/menu/hover-aim.js.map +0 -1
- package/dist/components/menu/menu.js.map +0 -1
- package/dist/components/popover/overview.demo.d.ts +0 -7
- package/dist/components/popover/overview.demo.js +0 -65
- package/dist/components/popover/overview.demo.js.map +0 -1
- package/dist/components/popover/popover.js.map +0 -1
- package/dist/components/select/deconstructed.demo.d.ts +0 -7
- package/dist/components/select/deconstructed.demo.js +0 -54
- package/dist/components/select/deconstructed.demo.js.map +0 -1
- package/dist/components/select/overview.demo.d.ts +0 -6
- package/dist/components/select/overview.demo.js +0 -28
- package/dist/components/select/overview.demo.js.map +0 -1
- package/dist/components/select/select.js.map +0 -1
- package/dist/components/separator/separator.d.ts +0 -2
- package/dist/components/separator/separator.js +0 -15
- package/dist/components/separator/separator.js.map +0 -1
- package/dist/components/tabs/tabs.d.ts +0 -78
- package/dist/components/tabs/tabs.js +0 -339
- package/dist/components/tabs/tabs.js.map +0 -1
- package/dist/interactions/keydown/keydown.js.map +0 -1
- package/dist/interactions/outside-click/outside-click-mixin.js.map +0 -1
- package/dist/interactions/typeahead/typeahead-mixin.js.map +0 -1
- package/dist/runtime/demos/basic.demo.d.ts +0 -6
- package/dist/runtime/demos/basic.demo.js +0 -16
- package/dist/runtime/demos/basic.demo.js.map +0 -1
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +0 -6
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +0 -77
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +0 -1
- package/dist/runtime/demos/keyed-list.demo.d.ts +0 -6
- package/dist/runtime/demos/keyed-list.demo.js +0 -62
- package/dist/runtime/demos/keyed-list.demo.js.map +0 -1
- package/dist/runtime/demos/readme.demo.d.ts +0 -5
- package/dist/runtime/demos/readme.demo.js +0 -396
- package/dist/runtime/demos/readme.demo.js.map +0 -1
- package/dist/theme/contract.d.ts +0 -61
- package/dist/theme/contract.js +0 -131
- package/dist/theme/contract.js.map +0 -1
- package/dist/theme/glyph-contract.d.ts +0 -11
- package/dist/theme/glyph-contract.js +0 -34
- package/dist/theme/glyph-contract.js.map +0 -1
- package/dist/theme/presets/rmx-01/glyphs.d.ts +0 -2
- package/dist/theme/presets/rmx-01/glyphs.js +0 -252
- package/dist/theme/presets/rmx-01/glyphs.js.map +0 -1
- package/dist/theme/presets/rmx-01/index.d.ts +0 -4
- package/dist/theme/presets/rmx-01/index.js.map +0 -1
- package/dist/theme/runtime.d.ts +0 -2
- package/dist/theme/runtime.js +0 -84
- package/dist/theme/runtime.js.map +0 -1
- package/dist/theme/theme.d.ts +0 -6
- package/dist/theme/theme.js +0 -5
- package/dist/theme/theme.js.map +0 -1
- package/dist/utils/flash-attribute.js.map +0 -1
- package/dist/utils/scroll-lock.d.ts +0 -3
- package/dist/utils/scroll-lock.js.map +0 -1
- package/dist/utils/wait-for-css-transition.js.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/src/animation/demos/animation.demo.tsx +0 -194
- package/src/animation/demos/spring.demo.tsx +0 -385
- package/src/components/accordion/accordion.tsx +0 -565
- package/src/components/accordion/demos/card.demo.tsx +0 -114
- package/src/components/accordion/demos/multiple.demo.tsx +0 -57
- package/src/components/accordion/demos/overview.demo.tsx +0 -57
- package/src/components/breadcrumbs/basic.demo.tsx +0 -18
- package/src/components/breadcrumbs/separator.demo.tsx +0 -19
- package/src/components/button/README.md +0 -44
- package/src/components/button/button.tsx +0 -286
- package/src/components/button/demos/aliases.demo.tsx +0 -28
- package/src/components/button/demos/basic.demo.tsx +0 -31
- package/src/components/button/demos/states.demo.tsx +0 -53
- package/src/components/combobox/overview.demo.tsx +0 -200
- package/src/components/glyph/README.md +0 -72
- package/src/components/glyph/glyph.tsx +0 -110
- package/src/components/listbox/overview.demo.tsx +0 -102
- package/src/components/menu/demos/bubbling.demo.tsx +0 -35
- package/src/components/menu/demos/context-trigger.demo.tsx +0 -133
- package/src/components/menu/demos/overview.demo.tsx +0 -77
- package/src/components/popover/overview.demo.tsx +0 -144
- package/src/components/select/deconstructed.demo.tsx +0 -96
- package/src/components/select/overview.demo.tsx +0 -52
- package/src/components/separator/separator.ts +0 -19
- package/src/components/tabs/README.md +0 -105
- package/src/components/tabs/tabs.tsx +0 -504
- package/src/runtime/demos/basic.demo.tsx +0 -21
- package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +0 -177
- package/src/runtime/demos/keyed-list.demo.tsx +0 -94
- package/src/theme/README.md +0 -103
- package/src/theme/contract.ts +0 -215
- package/src/theme/glyph-contract.ts +0 -59
- package/src/theme/presets/rmx-01/glyphs.tsx +0 -308
- package/src/theme/presets/rmx-01/index.ts +0 -123
- package/src/theme/runtime.ts +0 -119
- package/src/theme/theme.ts +0 -15
- package/src/utils/scroll-lock/README.md +0 -33
- /package/dist/{components/anchor/anchor.d.ts → anchor/index.d.ts} +0 -0
- /package/dist/{components/breadcrumbs/breadcrumbs.d.ts → breadcrumbs/index.d.ts} +0 -0
- /package/dist/{utils → listbox}/flash-attribute.d.ts +0 -0
- /package/dist/{utils → listbox}/flash-attribute.js +0 -0
- /package/dist/{components/menu → menu}/hover-aim.d.ts +0 -0
- /package/dist/{components/menu → menu}/hover-aim.js +0 -0
- /package/dist/{interactions/outside-click/outside-click-mixin.d.ts → popover/outside-click.d.ts} +0 -0
- /package/dist/{interactions/keydown → select}/keydown.d.ts +0 -0
- /package/dist/{interactions/keydown → select}/keydown.js +0 -0
- /package/dist/{interactions/typeahead/typeahead-mixin.d.ts → shared/typeahead.d.ts} +0 -0
- /package/dist/{utils → shared}/wait-for-css-transition.d.ts +0 -0
- /package/dist/{utils → shared}/wait-for-css-transition.js +0 -0
- /package/dist/{utils → shared}/wait.d.ts +0 -0
- /package/dist/{utils → shared}/wait.js +0 -0
- /package/src/{components/anchor/anchor.ts → anchor/index.ts} +0 -0
- /package/src/{utils → listbox}/flash-attribute.ts +0 -0
- /package/src/{components/menu → menu}/hover-aim.ts +0 -0
- /package/src/{interactions/outside-click/outside-click-mixin.ts → popover/outside-click.ts} +0 -0
- /package/src/{interactions/keydown → select}/keydown.ts +0 -0
- /package/src/{interactions/typeahead/typeahead-mixin.ts → shared/typeahead.ts} +0 -0
- /package/src/{utils → shared}/wait-for-css-transition.ts +0 -0
- /package/src/{utils → shared}/wait.ts +0 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attrs,
|
|
3
|
+
createMixin,
|
|
4
|
+
on,
|
|
5
|
+
ref,
|
|
6
|
+
type Dispatched,
|
|
7
|
+
type ElementProps,
|
|
8
|
+
type Handle,
|
|
9
|
+
type MixinHandle,
|
|
10
|
+
type Props,
|
|
11
|
+
type RemixNode,
|
|
12
|
+
} from '@remix-run/ui'
|
|
13
|
+
|
|
14
|
+
const ACCORDION_CHANGE_EVENT = 'rmx:accordion-change' as const
|
|
15
|
+
|
|
16
|
+
type AccordionChangeHandler<target extends HTMLElement> = (
|
|
17
|
+
event: Dispatched<AccordionChangeEvent, target>,
|
|
18
|
+
signal: AbortSignal,
|
|
19
|
+
) => void | Promise<void>
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementEventMap {
|
|
23
|
+
[ACCORDION_CHANGE_EVENT]: AccordionChangeEvent
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class AccordionChangeEvent extends Event {
|
|
28
|
+
readonly accordionType: AccordionType
|
|
29
|
+
readonly itemValue: string
|
|
30
|
+
readonly value: AccordionValue
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
value: AccordionValue,
|
|
34
|
+
init: {
|
|
35
|
+
accordionType: AccordionType
|
|
36
|
+
itemValue: string
|
|
37
|
+
},
|
|
38
|
+
) {
|
|
39
|
+
super(ACCORDION_CHANGE_EVENT, {
|
|
40
|
+
bubbles: true,
|
|
41
|
+
})
|
|
42
|
+
this.accordionType = init.accordionType
|
|
43
|
+
this.itemValue = init.itemValue
|
|
44
|
+
this.value = value
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type AccordionType = 'single' | 'multiple'
|
|
49
|
+
export type AccordionValue = string | null | string[]
|
|
50
|
+
export type AccordionSingleValue = string | null
|
|
51
|
+
export type AccordionMultipleValue = string[]
|
|
52
|
+
export type AccordionHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6
|
|
53
|
+
|
|
54
|
+
type FocusDirection = 'first' | 'last' | 'next' | 'previous'
|
|
55
|
+
|
|
56
|
+
type RegisteredItem = {
|
|
57
|
+
disabled: boolean
|
|
58
|
+
getTriggerNode(): HTMLButtonElement | null
|
|
59
|
+
value: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface AccordionContextValue {
|
|
63
|
+
readonly collapsible: boolean
|
|
64
|
+
readonly disabled: boolean
|
|
65
|
+
readonly headingLevel: AccordionHeadingLevel
|
|
66
|
+
readonly type: AccordionType
|
|
67
|
+
focusItem(value: string, direction: FocusDirection): void
|
|
68
|
+
getPanelId(value: string): string
|
|
69
|
+
getTriggerId(value: string): string | undefined
|
|
70
|
+
isOpen(value: string): boolean
|
|
71
|
+
registerItem(item: RegisteredItem): void
|
|
72
|
+
registerRoot(node: HTMLElement): void
|
|
73
|
+
toggleItem(value: string): void
|
|
74
|
+
unregisterRoot(node: HTMLElement): void
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface AccordionItemContextValue {
|
|
78
|
+
readonly disabled: boolean
|
|
79
|
+
readonly headingLevel: AccordionHeadingLevel
|
|
80
|
+
readonly lockedOpen: boolean
|
|
81
|
+
readonly open: boolean
|
|
82
|
+
readonly panelId: string
|
|
83
|
+
readonly triggerId: string
|
|
84
|
+
readonly value: string
|
|
85
|
+
setTriggerNode(node: HTMLButtonElement | null): void
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AccordionBaseContextProps {
|
|
89
|
+
children?: RemixNode
|
|
90
|
+
disabled?: boolean
|
|
91
|
+
headingLevel?: AccordionHeadingLevel
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface AccordionSingleContextProps extends AccordionBaseContextProps {
|
|
95
|
+
type?: 'single'
|
|
96
|
+
value?: AccordionSingleValue
|
|
97
|
+
defaultValue?: AccordionSingleValue
|
|
98
|
+
onValueChange?: (value: AccordionSingleValue) => void
|
|
99
|
+
collapsible?: boolean
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface AccordionMultipleContextProps extends AccordionBaseContextProps {
|
|
103
|
+
type: 'multiple'
|
|
104
|
+
value?: AccordionMultipleValue
|
|
105
|
+
defaultValue?: AccordionMultipleValue
|
|
106
|
+
onValueChange?: (value: AccordionMultipleValue) => void
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type AccordionContextProps = AccordionSingleContextProps | AccordionMultipleContextProps
|
|
110
|
+
|
|
111
|
+
export interface AccordionRootOptions {}
|
|
112
|
+
|
|
113
|
+
export interface AccordionItemOptions {
|
|
114
|
+
disabled?: boolean
|
|
115
|
+
value: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface AccordionTriggerOptions {
|
|
119
|
+
disabled?: boolean
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface AccordionContentOptions {}
|
|
123
|
+
|
|
124
|
+
export type AccordionProps = Props<'div'> & AccordionContextProps
|
|
125
|
+
|
|
126
|
+
export type AccordionItemProps = Omit<Props<'div'>, 'children'> &
|
|
127
|
+
AccordionItemOptions & {
|
|
128
|
+
children?: RemixNode
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export type AccordionTriggerProps = Omit<Props<'button'>, 'children' | 'type'> &
|
|
132
|
+
AccordionTriggerOptions & {
|
|
133
|
+
children?: RemixNode
|
|
134
|
+
type?: 'button' | 'submit' | 'reset'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type AccordionContentProps = Omit<Props<'div'>, 'children'> & {
|
|
138
|
+
children?: RemixNode
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isMultipleProps(
|
|
142
|
+
props: AccordionContextProps | null,
|
|
143
|
+
): props is AccordionMultipleContextProps {
|
|
144
|
+
return props?.type === 'multiple'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getAccordionContext(handle: Handle<unknown, unknown> | MixinHandle) {
|
|
148
|
+
return handle.context.get(AccordionProvider)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getAccordionItemContext(handle: Handle<unknown, unknown> | MixinHandle) {
|
|
152
|
+
return handle.context.get(AccordionItemProvider)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function AccordionProvider(
|
|
156
|
+
handle: Handle<AccordionContextProps, AccordionContextValue>,
|
|
157
|
+
): () => RemixNode {
|
|
158
|
+
let rootNode: HTMLElement | null = null
|
|
159
|
+
let registeredItems: RegisteredItem[] = []
|
|
160
|
+
let uncontrolledSingleValue: string | null = null
|
|
161
|
+
let uncontrolledMultipleValue: string[] = []
|
|
162
|
+
let hasInitializedSingle = false
|
|
163
|
+
let hasInitializedMultiple = false
|
|
164
|
+
|
|
165
|
+
function getType() {
|
|
166
|
+
return isMultipleProps(handle.props) ? 'multiple' : 'single'
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getSingleValue() {
|
|
170
|
+
if (isMultipleProps(handle.props)) {
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (handle.props.value !== undefined) {
|
|
175
|
+
return handle.props.value
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!hasInitializedSingle) {
|
|
179
|
+
uncontrolledSingleValue = handle.props.defaultValue ?? null
|
|
180
|
+
hasInitializedSingle = true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return uncontrolledSingleValue
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getMultipleValue() {
|
|
187
|
+
if (!isMultipleProps(handle.props)) {
|
|
188
|
+
return []
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (handle.props.value !== undefined) {
|
|
192
|
+
return handle.props.value
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!hasInitializedMultiple) {
|
|
196
|
+
uncontrolledMultipleValue = [...(handle.props.defaultValue ?? [])]
|
|
197
|
+
hasInitializedMultiple = true
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return uncontrolledMultipleValue
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isOpen(value: string) {
|
|
204
|
+
if (getType() === 'multiple') {
|
|
205
|
+
return getMultipleValue().includes(value)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return getSingleValue() === value
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function dispatchChange(itemValue: string, value: AccordionValue) {
|
|
212
|
+
rootNode?.dispatchEvent(
|
|
213
|
+
new AccordionChangeEvent(value, {
|
|
214
|
+
accordionType: getType(),
|
|
215
|
+
itemValue,
|
|
216
|
+
}),
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function toggleItem(itemValue: string) {
|
|
221
|
+
if (handle.props.disabled) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (isMultipleProps(handle.props)) {
|
|
226
|
+
let currentValue = getMultipleValue()
|
|
227
|
+
let nextValue = currentValue.includes(itemValue)
|
|
228
|
+
? currentValue.filter((value) => value !== itemValue)
|
|
229
|
+
: [...currentValue, itemValue]
|
|
230
|
+
|
|
231
|
+
if (handle.props.value === undefined) {
|
|
232
|
+
uncontrolledMultipleValue = nextValue
|
|
233
|
+
void handle.update()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
handle.props.onValueChange?.(nextValue)
|
|
237
|
+
dispatchChange(itemValue, nextValue)
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let isCurrentItemOpen = getSingleValue() === itemValue
|
|
242
|
+
if (isCurrentItemOpen && !(handle.props.collapsible ?? true)) {
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let nextValue = isCurrentItemOpen ? null : itemValue
|
|
247
|
+
|
|
248
|
+
if (handle.props.value === undefined) {
|
|
249
|
+
uncontrolledSingleValue = nextValue
|
|
250
|
+
void handle.update()
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
handle.props.onValueChange?.(nextValue)
|
|
254
|
+
dispatchChange(itemValue, nextValue)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function focusItem(itemValue: string, direction: FocusDirection) {
|
|
258
|
+
let items = registeredItems.filter((item) => !item.disabled && item.getTriggerNode() !== null)
|
|
259
|
+
if (items.length === 0) {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let currentIndex = items.findIndex((item) => item.value === itemValue)
|
|
264
|
+
let targetIndex = 0
|
|
265
|
+
|
|
266
|
+
switch (direction) {
|
|
267
|
+
case 'first':
|
|
268
|
+
targetIndex = 0
|
|
269
|
+
break
|
|
270
|
+
case 'last':
|
|
271
|
+
targetIndex = items.length - 1
|
|
272
|
+
break
|
|
273
|
+
case 'previous':
|
|
274
|
+
targetIndex = currentIndex <= 0 ? items.length - 1 : currentIndex - 1
|
|
275
|
+
break
|
|
276
|
+
case 'next':
|
|
277
|
+
targetIndex =
|
|
278
|
+
currentIndex === -1 || currentIndex === items.length - 1 ? 0 : currentIndex + 1
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
items[targetIndex]?.getTriggerNode()?.focus()
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function registerItem(item: RegisteredItem) {
|
|
286
|
+
registeredItems.push(item)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function getTriggerId(value: string) {
|
|
290
|
+
return registeredItems.find((item) => item.value === value)?.getTriggerNode()?.id
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function getPanelId(value: string) {
|
|
294
|
+
return `${handle.id}-${value}-panel`
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
handle.context.set({
|
|
298
|
+
get collapsible() {
|
|
299
|
+
return getType() === 'multiple'
|
|
300
|
+
? true
|
|
301
|
+
: ((handle.props as AccordionSingleContextProps).collapsible ?? true)
|
|
302
|
+
},
|
|
303
|
+
get disabled() {
|
|
304
|
+
return handle.props.disabled ?? false
|
|
305
|
+
},
|
|
306
|
+
focusItem,
|
|
307
|
+
getPanelId,
|
|
308
|
+
getTriggerId,
|
|
309
|
+
get headingLevel() {
|
|
310
|
+
return handle.props.headingLevel ?? 3
|
|
311
|
+
},
|
|
312
|
+
isOpen,
|
|
313
|
+
registerItem,
|
|
314
|
+
registerRoot(node) {
|
|
315
|
+
rootNode = node
|
|
316
|
+
},
|
|
317
|
+
toggleItem,
|
|
318
|
+
get type() {
|
|
319
|
+
return getType()
|
|
320
|
+
},
|
|
321
|
+
unregisterRoot(node) {
|
|
322
|
+
if (rootNode === node) {
|
|
323
|
+
rootNode = null
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
return () => {
|
|
329
|
+
registeredItems = []
|
|
330
|
+
return handle.props.children
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function AccordionItemProvider(
|
|
335
|
+
handle: Handle<AccordionItemOptions & { children?: RemixNode }, AccordionItemContextValue>,
|
|
336
|
+
): () => RemixNode {
|
|
337
|
+
let triggerNode: HTMLButtonElement | null = null
|
|
338
|
+
let triggerId = `${handle.id}-trigger`
|
|
339
|
+
let panelId = `${handle.id}-panel`
|
|
340
|
+
let disabled = false
|
|
341
|
+
let headingLevel: AccordionHeadingLevel = 3
|
|
342
|
+
let lockedOpen = false
|
|
343
|
+
let open = false
|
|
344
|
+
let value = handle.props.value
|
|
345
|
+
|
|
346
|
+
handle.context.set({
|
|
347
|
+
get disabled() {
|
|
348
|
+
return disabled
|
|
349
|
+
},
|
|
350
|
+
get headingLevel() {
|
|
351
|
+
return headingLevel
|
|
352
|
+
},
|
|
353
|
+
get lockedOpen() {
|
|
354
|
+
return lockedOpen
|
|
355
|
+
},
|
|
356
|
+
get open() {
|
|
357
|
+
return open
|
|
358
|
+
},
|
|
359
|
+
get panelId() {
|
|
360
|
+
return panelId
|
|
361
|
+
},
|
|
362
|
+
setTriggerNode(node) {
|
|
363
|
+
triggerNode = node
|
|
364
|
+
},
|
|
365
|
+
get triggerId() {
|
|
366
|
+
return triggerId
|
|
367
|
+
},
|
|
368
|
+
get value() {
|
|
369
|
+
return value
|
|
370
|
+
},
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
return () => {
|
|
374
|
+
let accordion = getAccordionContext(handle)
|
|
375
|
+
value = handle.props.value
|
|
376
|
+
disabled = accordion.disabled || handle.props.disabled === true
|
|
377
|
+
headingLevel = accordion.headingLevel
|
|
378
|
+
open = accordion.isOpen(handle.props.value)
|
|
379
|
+
lockedOpen = accordion.type === 'single' && !accordion.collapsible && open
|
|
380
|
+
|
|
381
|
+
accordion.registerItem({
|
|
382
|
+
disabled,
|
|
383
|
+
getTriggerNode: () => triggerNode,
|
|
384
|
+
value: handle.props.value,
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
return handle.props.children
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const rootMixin = createMixin<HTMLElement, [options?: AccordionRootOptions], ElementProps>(
|
|
392
|
+
(handle) => {
|
|
393
|
+
let context = getAccordionContext(handle)
|
|
394
|
+
let rootNode: HTMLElement | null = null
|
|
395
|
+
|
|
396
|
+
handle.queueTask((node, signal) => {
|
|
397
|
+
rootNode = node
|
|
398
|
+
context.registerRoot(node)
|
|
399
|
+
signal.addEventListener('abort', () => {
|
|
400
|
+
if (rootNode === node) {
|
|
401
|
+
rootNode = null
|
|
402
|
+
}
|
|
403
|
+
context.unregisterRoot(node)
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
return () =>
|
|
408
|
+
attrs({
|
|
409
|
+
'data-disabled': context.disabled ? '' : undefined,
|
|
410
|
+
'data-type': context.type,
|
|
411
|
+
})
|
|
412
|
+
},
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
const itemMixin = createMixin<HTMLElement, [options?: AccordionItemOptions], ElementProps>(
|
|
416
|
+
(handle) => {
|
|
417
|
+
let item = getAccordionItemContext(handle)
|
|
418
|
+
|
|
419
|
+
return () =>
|
|
420
|
+
attrs({
|
|
421
|
+
'data-disabled': item.disabled ? '' : undefined,
|
|
422
|
+
'data-state': item.open ? 'open' : 'closed',
|
|
423
|
+
})
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
const triggerMixin = createMixin<
|
|
428
|
+
HTMLButtonElement,
|
|
429
|
+
[options?: AccordionTriggerOptions],
|
|
430
|
+
ElementProps
|
|
431
|
+
>((handle) => {
|
|
432
|
+
let accordion = getAccordionContext(handle)
|
|
433
|
+
let item = getAccordionItemContext(handle)
|
|
434
|
+
|
|
435
|
+
return (options, props = options as ElementProps) => {
|
|
436
|
+
options = props === options ? undefined : options
|
|
437
|
+
let disabled = item.disabled || options?.disabled === true || props.disabled === true
|
|
438
|
+
let toggleItem = () => {
|
|
439
|
+
if (disabled || item.lockedOpen) {
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
accordion.toggleItem(item.value)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return [
|
|
447
|
+
attrs({
|
|
448
|
+
'aria-controls': item.panelId,
|
|
449
|
+
'aria-disabled': item.lockedOpen ? true : undefined,
|
|
450
|
+
'aria-expanded': item.open,
|
|
451
|
+
'data-state': item.open ? 'open' : 'closed',
|
|
452
|
+
disabled: disabled ? true : undefined,
|
|
453
|
+
id: item.triggerId,
|
|
454
|
+
}),
|
|
455
|
+
ref((node: HTMLButtonElement) => {
|
|
456
|
+
item.setTriggerNode(node)
|
|
457
|
+
}),
|
|
458
|
+
on('click', toggleItem),
|
|
459
|
+
on('keydown', (event) => {
|
|
460
|
+
switch (event.key) {
|
|
461
|
+
case 'ArrowDown':
|
|
462
|
+
event.preventDefault()
|
|
463
|
+
accordion.focusItem(item.value, 'next')
|
|
464
|
+
break
|
|
465
|
+
case 'ArrowUp':
|
|
466
|
+
event.preventDefault()
|
|
467
|
+
accordion.focusItem(item.value, 'previous')
|
|
468
|
+
break
|
|
469
|
+
case 'Home':
|
|
470
|
+
event.preventDefault()
|
|
471
|
+
accordion.focusItem(item.value, 'first')
|
|
472
|
+
break
|
|
473
|
+
case 'End':
|
|
474
|
+
event.preventDefault()
|
|
475
|
+
accordion.focusItem(item.value, 'last')
|
|
476
|
+
break
|
|
477
|
+
}
|
|
478
|
+
}),
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
const contentMixin = createMixin<HTMLElement, [options?: AccordionContentOptions], ElementProps>(
|
|
484
|
+
(handle) => {
|
|
485
|
+
let item = getAccordionItemContext(handle)
|
|
486
|
+
|
|
487
|
+
return () =>
|
|
488
|
+
attrs({
|
|
489
|
+
'aria-hidden': item.open ? undefined : true,
|
|
490
|
+
'aria-labelledby': item.triggerId,
|
|
491
|
+
'data-state': item.open ? 'open' : 'closed',
|
|
492
|
+
id: item.panelId,
|
|
493
|
+
inert: item.open ? undefined : true,
|
|
494
|
+
})
|
|
495
|
+
},
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
export const Context = AccordionProvider
|
|
499
|
+
export const ItemContext: (
|
|
500
|
+
handle: Handle<AccordionItemOptions & { children?: RemixNode }, AccordionItemContextValue>,
|
|
501
|
+
) => () => RemixNode = AccordionItemProvider
|
|
502
|
+
export const content = contentMixin
|
|
503
|
+
export const item = itemMixin
|
|
504
|
+
export const root = rootMixin
|
|
505
|
+
export const trigger = triggerMixin
|
|
506
|
+
|
|
507
|
+
export function onAccordionChange<target extends HTMLElement>(
|
|
508
|
+
handler: AccordionChangeHandler<target>,
|
|
509
|
+
captureBoolean?: boolean,
|
|
510
|
+
): ReturnType<typeof on<target, typeof ACCORDION_CHANGE_EVENT>> {
|
|
511
|
+
return on(ACCORDION_CHANGE_EVENT, handler, captureBoolean)
|
|
512
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`anchor` positions a floating element against an anchor element or viewport coordinates and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
|
|
4
4
|
|
|
5
|
-
## Usage
|
|
5
|
+
## Primitive Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import { anchor } from 'remix/ui/anchor'
|
|
@@ -55,7 +55,42 @@ Anchor to coordinates when the surface should open at a pointer location.
|
|
|
55
55
|
let cleanup = anchor(popover, { x: event.clientX, y: event.clientY }, { placement: 'bottom-start' })
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Keep presentation app-owned when the anchored element is rendered by your component:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { anchor } from 'remix/ui/anchor'
|
|
62
|
+
import { on, ref, type Handle } from 'remix/ui'
|
|
63
|
+
import { panelStyle } from './floating.styles'
|
|
64
|
+
|
|
65
|
+
export function AnchoredPanel(handle: Handle) {
|
|
66
|
+
let cleanup = () => {}
|
|
67
|
+
let trigger: HTMLElement | null = null
|
|
68
|
+
let panel: HTMLElement | null = null
|
|
69
|
+
|
|
70
|
+
function position() {
|
|
71
|
+
cleanup()
|
|
72
|
+
if (trigger && panel) {
|
|
73
|
+
cleanup = anchor(panel, trigger, { placement: 'bottom-start', offset: 8 })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return () => (
|
|
78
|
+
<>
|
|
79
|
+
<button
|
|
80
|
+
mix={[ref((node) => (trigger = node as HTMLElement)), on('click', position)]}
|
|
81
|
+
type="button"
|
|
82
|
+
>
|
|
83
|
+
Open
|
|
84
|
+
</button>
|
|
85
|
+
<div data-panel mix={[panelStyle, ref((node) => (panel = node as HTMLElement))]}>
|
|
86
|
+
Panel
|
|
87
|
+
</div>
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## `remix/ui/anchor`
|
|
59
94
|
|
|
60
95
|
- `anchor(floatingElement, anchorTarget, options)`: positions `floatingElement` against an element or coordinate target, starts animation-frame polling for geometry changes, and returns a cleanup function.
|
|
61
96
|
- `AnchorOptions`: placement, inset, relative alignment, and offset options.
|
|
@@ -15,16 +15,19 @@ function shuffle<T>(array: T[]): T[] {
|
|
|
15
15
|
export function Reordering(handle: Handle) {
|
|
16
16
|
let order = initialOrder
|
|
17
17
|
|
|
18
|
-
function scheduleNextShuffle() {
|
|
19
|
-
setTimeout(() => {
|
|
20
|
-
if (
|
|
18
|
+
function scheduleNextShuffle(signal: AbortSignal) {
|
|
19
|
+
let timeoutId = setTimeout(async () => {
|
|
20
|
+
if (signal.aborted) return
|
|
21
21
|
order = shuffle(order)
|
|
22
|
-
handle.update()
|
|
23
|
-
|
|
22
|
+
let nextSignal = await handle.update()
|
|
23
|
+
if (nextSignal.aborted) return
|
|
24
|
+
scheduleNextShuffle(nextSignal)
|
|
24
25
|
}, 1000)
|
|
26
|
+
|
|
27
|
+
signal.addEventListener('abort', () => clearTimeout(timeoutId), { once: true })
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
scheduleNextShuffle
|
|
30
|
+
handle.queueTask(scheduleNextShuffle)
|
|
28
31
|
|
|
29
32
|
return () => (
|
|
30
33
|
<ul
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`Breadcrumbs` renders semantic breadcrumb navigation from a list of items. Use it when the page needs a compact path back through parent sections.
|
|
4
4
|
|
|
5
|
-
## Usage
|
|
5
|
+
## Component Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import { Breadcrumbs } from 'remix/ui/breadcrumbs'
|
|
@@ -32,13 +32,13 @@ Mark an earlier item as current when the page belongs to a parent section but th
|
|
|
32
32
|
/>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Pass `separator` to replace the default chevron
|
|
35
|
+
Pass `separator` to replace the default chevron icon.
|
|
36
36
|
|
|
37
37
|
```tsx
|
|
38
38
|
<Breadcrumbs items={[{ href: '/', label: 'Home' }, { label: 'Breadcrumbs' }]} separator="/" />
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
## `breadcrumbs
|
|
41
|
+
## `remix/ui/breadcrumbs`
|
|
42
42
|
|
|
43
43
|
- `Breadcrumbs`: component that renders a `<nav>` with an ordered list of breadcrumb items.
|
|
44
44
|
- `BreadcrumbItem`: item shape with `label`, optional `href`, and optional `current`.
|
|
@@ -52,4 +52,4 @@ Pass `separator` to replace the default chevron glyph.
|
|
|
52
52
|
- An explicit current item wins over the last-item default.
|
|
53
53
|
- Current items render as text with `aria-current="page"`, even when they include `href`.
|
|
54
54
|
- Non-current items with `href` render as links; non-current items without `href` render as text.
|
|
55
|
-
- The default separator is
|
|
55
|
+
- The default separator is a chevron icon. Separators render between items and are hidden from assistive technology.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { css, createElement } from '@remix-run/ui'
|
|
2
2
|
import type { Handle, Props, RemixNode } from '@remix-run/ui'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ChevronRightIcon } from '../shared/icons.tsx'
|
|
5
|
+
import { componentStyleValues as styles } from '../shared/style-values.ts'
|
|
6
6
|
|
|
7
7
|
export type BreadcrumbItem = {
|
|
8
8
|
current?: boolean
|
|
@@ -23,7 +23,7 @@ const listCss = css({
|
|
|
23
23
|
display: 'flex',
|
|
24
24
|
flexWrap: 'wrap',
|
|
25
25
|
alignItems: 'center',
|
|
26
|
-
gap: `${
|
|
26
|
+
gap: `${styles.space.xs} ${styles.space.sm}`,
|
|
27
27
|
minWidth: 0,
|
|
28
28
|
margin: 0,
|
|
29
29
|
padding: 0,
|
|
@@ -41,34 +41,34 @@ const separatorCss = css({
|
|
|
41
41
|
alignItems: 'center',
|
|
42
42
|
justifyContent: 'center',
|
|
43
43
|
flexShrink: 0,
|
|
44
|
-
width:
|
|
45
|
-
height:
|
|
46
|
-
color:
|
|
44
|
+
width: styles.fontSize.sm,
|
|
45
|
+
height: styles.fontSize.sm,
|
|
46
|
+
color: styles.colors.text.muted,
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const linkCss = css({
|
|
50
|
-
color:
|
|
51
|
-
fontSize:
|
|
52
|
-
lineHeight:
|
|
50
|
+
color: styles.colors.text.secondary,
|
|
51
|
+
fontSize: styles.fontSize.sm,
|
|
52
|
+
lineHeight: styles.lineHeight.normal,
|
|
53
53
|
textDecoration: 'none',
|
|
54
54
|
whiteSpace: 'nowrap',
|
|
55
55
|
'&:hover': {
|
|
56
|
-
color:
|
|
56
|
+
color: styles.colors.text.primary,
|
|
57
57
|
},
|
|
58
58
|
})
|
|
59
59
|
|
|
60
60
|
const currentCss = css({
|
|
61
|
-
color:
|
|
62
|
-
fontSize:
|
|
63
|
-
lineHeight:
|
|
64
|
-
fontWeight:
|
|
61
|
+
color: styles.colors.text.primary,
|
|
62
|
+
fontSize: styles.fontSize.sm,
|
|
63
|
+
lineHeight: styles.lineHeight.normal,
|
|
64
|
+
fontWeight: styles.fontWeight.medium,
|
|
65
65
|
whiteSpace: 'nowrap',
|
|
66
66
|
})
|
|
67
67
|
|
|
68
68
|
const textCss = css({
|
|
69
|
-
color:
|
|
70
|
-
fontSize:
|
|
71
|
-
lineHeight:
|
|
69
|
+
color: styles.colors.text.secondary,
|
|
70
|
+
fontSize: styles.fontSize.sm,
|
|
71
|
+
lineHeight: styles.lineHeight.normal,
|
|
72
72
|
whiteSpace: 'nowrap',
|
|
73
73
|
})
|
|
74
74
|
|
|
@@ -80,7 +80,7 @@ export function Breadcrumbs(handle: Handle<BreadcrumbsProps>): () => RemixNode {
|
|
|
80
80
|
currentIndex = Math.max(0, items.length - 1)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
let separatorContent = separator ?? <
|
|
83
|
+
let separatorContent = separator ?? <ChevronRightIcon />
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<nav aria-label={ariaLabel ?? 'Breadcrumb'} {...navProps} mix={[rootCss, mix]}>
|