@remix-run/ui 0.4.0 → 0.6.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 +74 -1
- package/dist/accordion/index.js +2 -2
- package/dist/accordion/primitives.d.ts +1 -1
- package/dist/accordion/primitives.js.map +1 -1
- package/dist/anchor/index.js.map +1 -1
- package/dist/animation/animate-layout-mixin.js +1 -1
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/animate-mixins.js +2 -2
- package/dist/animation/animate-mixins.js.map +1 -1
- package/dist/animation/demos/color-interpolation.js +1 -1
- package/dist/animation/demos/color-interpolation.js.map +1 -1
- package/dist/animation/demos/drag-release.d.ts +1 -1
- package/dist/animation/demos/hold-to-confirm.d.ts +3 -3
- package/dist/animation/demos/material-ripple.js +1 -3
- package/dist/animation/demos/material-ripple.js.map +1 -1
- package/dist/animation/index.js +4 -4
- package/dist/animation/spring.js.map +1 -1
- package/dist/breadcrumbs/index.js +2 -2
- package/dist/button/index.js +25 -25
- package/dist/button/index.js.map +1 -1
- package/dist/checkbox/index.js +11 -9
- package/dist/checkbox/index.js.map +1 -1
- package/dist/combobox/index.js +4 -4
- package/dist/combobox/index.js.map +1 -1
- package/dist/combobox/primitives.d.ts +1 -1
- package/dist/combobox/primitives.js +5 -5
- package/dist/combobox/primitives.js.map +1 -1
- package/dist/dev/refresh.d.ts +1 -0
- package/dist/dev/refresh.js +2 -0
- package/dist/dev/refresh.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -14
- package/dist/input/index.js +9 -9
- package/dist/input/index.js.map +1 -1
- package/dist/jsx-runtime.js +2 -2
- package/dist/listbox/index.js +2 -2
- package/dist/menu/hover-aim.js.map +1 -1
- package/dist/menu/index.js +3 -3
- package/dist/menu/primitives.d.ts +1 -1
- package/dist/menu/primitives.js +6 -6
- package/dist/menu/primitives.js.map +1 -1
- package/dist/popover/index.js +3 -3
- package/dist/popover/scroll-lock.js.map +1 -1
- package/dist/radio/index.js +7 -7
- package/dist/radio/index.js.map +1 -1
- package/dist/runtime/client-entry-boundary.d.ts +14 -0
- package/dist/runtime/client-entry-boundary.js +29 -0
- package/dist/runtime/client-entry-boundary.js.map +1 -0
- package/dist/runtime/component.d.ts +19 -4
- package/dist/runtime/component.js +39 -9
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.js +36 -3
- package/dist/runtime/core/attributes.js.map +1 -1
- package/dist/runtime/core/children.d.ts +2 -0
- package/dist/runtime/core/children.js +26 -2
- package/dist/runtime/core/children.js.map +1 -1
- package/dist/runtime/core/props.js +2 -2
- package/dist/runtime/core/vnode.d.ts +3 -3
- package/dist/runtime/core/vnode.js +22 -1
- package/dist/runtime/core/vnode.js.map +1 -1
- package/dist/runtime/create-element.d.ts +2 -2
- package/dist/runtime/create-element.js +3 -2
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +307 -220
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.js +3 -3
- package/dist/runtime/dom.d.ts +12 -0
- package/dist/runtime/element-function.d.ts +3 -0
- package/dist/runtime/element-function.js +2 -0
- package/dist/runtime/element-function.js.map +1 -0
- package/dist/runtime/form-navigation.d.ts +25 -0
- package/dist/runtime/form-navigation.js +132 -0
- package/dist/runtime/form-navigation.js.map +1 -0
- package/dist/runtime/frame-resolution.d.ts +5 -0
- package/dist/runtime/frame-resolution.js +10 -0
- package/dist/runtime/frame-resolution.js.map +1 -0
- package/dist/runtime/frame.d.ts +59 -15
- package/dist/runtime/frame.js +383 -114
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.d.ts +1 -1
- package/dist/runtime/jsx.js +1 -1
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/key.d.ts +1 -0
- package/dist/runtime/key.js +2 -0
- package/dist/runtime/key.js.map +1 -0
- package/dist/runtime/mixins/attrs-mixin.js +2 -2
- package/dist/runtime/mixins/link-mixin.js +4 -3
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +15 -10
- package/dist/runtime/mixins/mixin.js +4 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.js +1 -1
- package/dist/runtime/mixins/ref-mixin.js +1 -1
- package/dist/runtime/module-preloader.d.ts +8 -0
- package/dist/runtime/module-preloader.js +95 -0
- package/dist/runtime/module-preloader.js.map +1 -0
- package/dist/runtime/navigation.d.ts +4 -1
- package/dist/runtime/navigation.js +157 -32
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +11 -10
- package/dist/runtime/reconcile.js +549 -421
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/refresh.d.ts +28 -0
- package/dist/runtime/refresh.js +31 -0
- package/dist/runtime/refresh.js.map +1 -0
- package/dist/runtime/render.js +1 -1
- package/dist/runtime/render.js.map +1 -1
- package/dist/runtime/run.d.ts +3 -1
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.js +8 -16
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/to-vnode.d.ts +2 -2
- package/dist/runtime/to-vnode.js +68 -20
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vdom.d.ts +4 -2
- package/dist/runtime/vdom.js +87 -42
- package/dist/runtime/vdom.js.map +1 -1
- package/dist/runtime/vnode.d.ts +138 -67
- package/dist/runtime/vnode.js +17 -12
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/select/index.js +11 -11
- package/dist/select/index.js.map +1 -1
- package/dist/select/primitives.d.ts +1 -1
- package/dist/select/primitives.js +6 -6
- package/dist/server/stream.d.ts +2 -0
- package/dist/server/stream.js +74 -29
- package/dist/server/stream.js.map +1 -1
- package/dist/shared/focus-styles.d.ts +1 -1
- package/dist/shared/focus-styles.js +1 -1
- package/dist/shared/focus-styles.js.map +1 -1
- package/dist/shared/listbox-popover-styles.js +1 -1
- package/dist/shared/style-values.js +26 -26
- package/dist/shared/style-values.js.map +1 -1
- package/dist/style/css-mixin.js +3 -3
- package/dist/style/index.js +2 -2
- package/dist/style/style.js +25 -6
- package/dist/style/style.js.map +1 -1
- package/dist/style/stylesheet.d.ts +0 -1
- package/dist/style/stylesheet.js +13 -54
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/tabs/index.js +11 -11
- package/dist/tabs/index.js.map +1 -1
- package/dist/tabs/primitives.d.ts +1 -1
- package/dist/test.js +1 -1
- package/dist/toggle/index.js +9 -9
- package/dist/toggle/index.js.map +1 -1
- package/dist/toggle/primitives.d.ts +1 -1
- package/package.json +10 -7
- package/src/animation/animate-mixins.ts +3 -3
- package/src/animation/demos/color-interpolation.tsx +1 -1
- package/src/button/index.ts +24 -24
- package/src/checkbox/index.ts +10 -8
- package/src/combobox/index.tsx +1 -1
- package/src/dev/refresh.ts +1 -0
- package/src/index.ts +1 -1
- package/src/input/index.ts +8 -8
- package/src/radio/index.ts +6 -5
- package/src/runtime/client-entry-boundary.ts +54 -0
- package/src/runtime/component.ts +65 -16
- package/src/runtime/core/attributes.ts +35 -1
- package/src/runtime/core/children.ts +24 -2
- package/src/runtime/core/vnode.ts +25 -4
- package/src/runtime/create-element.ts +6 -5
- package/src/runtime/demos/readme.demo.tsx +4 -4
- package/src/runtime/diff-dom.ts +370 -244
- package/src/runtime/dom.ts +331 -214
- package/src/runtime/element-function.ts +1 -0
- package/src/runtime/event-listeners.ts +1 -1
- package/src/runtime/form-navigation.ts +184 -0
- package/src/runtime/frame-resolution.ts +13 -0
- package/src/runtime/frame.ts +500 -124
- package/src/runtime/jsx.ts +3 -5
- package/src/runtime/key.ts +1 -0
- package/src/runtime/mixins/link-mixin.ts +1 -0
- package/src/runtime/mixins/mixin.ts +21 -18
- package/src/runtime/module-preloader.ts +115 -0
- package/src/runtime/navigation.ts +204 -30
- package/src/runtime/reconcile.ts +762 -759
- package/src/runtime/refresh.ts +46 -0
- package/src/runtime/run.ts +12 -2
- package/src/runtime/scheduler.ts +9 -29
- package/src/runtime/to-vnode.ts +85 -23
- package/src/runtime/vdom.ts +89 -70
- package/src/runtime/vnode.ts +192 -88
- package/src/select/index.tsx +9 -9
- package/src/server/README.md +13 -0
- package/src/server/stream.ts +93 -30
- package/src/shared/focus-styles.ts +1 -1
- package/src/shared/style-values.ts +26 -26
- package/src/style/style.ts +25 -6
- package/src/style/stylesheet.ts +32 -57
- package/src/tabs/index.tsx +12 -11
- package/src/test/README.md +151 -60
- package/src/toggle/index.tsx +9 -7
|
@@ -37,7 +37,7 @@ type AnimationState = {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const animatingNodes = new WeakMap<Element, AnimationState>()
|
|
40
|
-
const initialEntranceSeenByParent = new WeakMap<ParentNode, Set<
|
|
40
|
+
const initialEntranceSeenByParent = new WeakMap<ParentNode, Set<unknown>>()
|
|
41
41
|
|
|
42
42
|
function extractStyleProps(config: AnimateMixinConfig): Keyframe {
|
|
43
43
|
let result: Keyframe = {}
|
|
@@ -152,14 +152,14 @@ function waitForAnimationOrAbort(animation: Animation, signal: AbortSignal): Pro
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
function shouldSkipInitialEntrance(
|
|
155
|
-
event: { key?:
|
|
155
|
+
event: { key?: unknown; parent: ParentNode },
|
|
156
156
|
config: AnimateMixinConfig,
|
|
157
157
|
): boolean {
|
|
158
158
|
if (config.initial !== false) return false
|
|
159
159
|
if (event.key == null) return false
|
|
160
160
|
let seenForParent = initialEntranceSeenByParent.get(event.parent)
|
|
161
161
|
if (!seenForParent) {
|
|
162
|
-
seenForParent = new Set<
|
|
162
|
+
seenForParent = new Set<unknown>()
|
|
163
163
|
initialEntranceSeenByParent.set(event.parent, seenForParent)
|
|
164
164
|
}
|
|
165
165
|
if (seenForParent.has(event.key)) return false
|
package/src/button/index.ts
CHANGED
|
@@ -31,7 +31,7 @@ const buttonDefaultAttrs = createMixin<Element, [], ElementProps>((handle, hostT
|
|
|
31
31
|
const baseStyle: CSSMixinDescriptor = css({
|
|
32
32
|
'--rmx-button-shadow': '0 0 0 0 rgba(0, 0, 0, 0)',
|
|
33
33
|
'--rmx-button-focus-shadow':
|
|
34
|
-
'0 0 0 1px #3573F6, var(--rmx-button-shadow), 0 0 0 4px rgba(53, 115, 246, 0.1), 0 6px 32px 4px rgba(53, 115, 246, 0.08), inset 0 0 8px 1px rgba(53, 115, 246, 0.05)',
|
|
34
|
+
'0 0 0 1px light-dark(#3573F6, #6eaaff), var(--rmx-button-shadow), 0 0 0 4px light-dark(rgba(53, 115, 246, 0.1), rgba(110, 170, 255, 0.18)), 0 6px 32px 4px light-dark(rgba(53, 115, 246, 0.08), rgba(110, 170, 255, 0.14)), inset 0 0 8px 1px light-dark(rgba(53, 115, 246, 0.05), rgba(110, 170, 255, 0.1))',
|
|
35
35
|
appearance: 'none',
|
|
36
36
|
margin: 0,
|
|
37
37
|
boxSizing: 'border-box',
|
|
@@ -85,49 +85,49 @@ const largeStyle: CSSMixinDescriptor = css({
|
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
const neutralStyle: CSSMixinDescriptor = css({
|
|
88
|
-
background: '#FCFCFC',
|
|
89
|
-
border: '1px solid rgba(0, 0, 0, 0.08)',
|
|
88
|
+
background: 'light-dark(#FCFCFC, #232323)',
|
|
89
|
+
border: '1px solid light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.14))',
|
|
90
90
|
'--rmx-button-shadow':
|
|
91
|
-
'0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.75), 0 1px 0 #FFFFFF, 0 2px 4px -1px rgb(0 0 0 / var(--rmx-button-neutral-shadow-alpha)), inset 0 2px 0 #FFFFFF, inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
|
|
92
|
-
color: '#101010',
|
|
93
|
-
textShadow: '0 1px 0 #FFFFFF',
|
|
91
|
+
'0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px light-dark(rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0.08)), 0 1px 0 light-dark(#FFFFFF, rgba(255, 255, 255, 0.04)), 0 2px 4px -1px rgb(0 0 0 / var(--rmx-button-neutral-shadow-alpha)), inset 0 2px 0 light-dark(#FFFFFF, rgba(255, 255, 255, 0.08)), inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
|
|
92
|
+
color: 'light-dark(#101010, #ececec)',
|
|
93
|
+
textShadow: '0 1px 0 light-dark(#FFFFFF, rgb(0 0 0 / 0.35))',
|
|
94
94
|
'&:hover:not(:disabled):not([aria-disabled="true"])': {
|
|
95
|
-
background: '#FFFFFF',
|
|
96
|
-
borderColor: 'rgba(0, 0, 0, 0.1)',
|
|
95
|
+
background: 'light-dark(#FFFFFF, #272727)',
|
|
96
|
+
borderColor: 'light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.18))',
|
|
97
97
|
'--rmx-button-shadow':
|
|
98
|
-
'0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.8), 0 1px 0 #FFFFFF, 0 3px 6px -2px rgb(0 0 0 / calc(var(--rmx-button-neutral-shadow-alpha) + 0.03)), inset 0 2px 0 #FFFFFF, inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
|
|
98
|
+
'0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.1)), 0 1px 0 light-dark(#FFFFFF, rgba(255, 255, 255, 0.05)), 0 3px 6px -2px rgb(0 0 0 / calc(var(--rmx-button-neutral-shadow-alpha) + 0.03)), inset 0 2px 0 light-dark(#FFFFFF, rgba(255, 255, 255, 0.1)), inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
|
|
99
99
|
},
|
|
100
100
|
'&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
|
|
101
101
|
{
|
|
102
|
-
background: '#F7F7F7',
|
|
103
|
-
borderColor: 'rgba(0, 0, 0, 0.12)',
|
|
102
|
+
background: 'light-dark(#F7F7F7, #1f1f1f)',
|
|
103
|
+
borderColor: 'light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))',
|
|
104
104
|
'--rmx-button-shadow':
|
|
105
|
-
'0 0 0 1px rgba(255, 255, 255, 0.65), inset 0 1px 2px rgba(0, 0, 0, 0.
|
|
105
|
+
'0 0 0 1px light-dark(rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0.08)), inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 -1px 0 light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.05))',
|
|
106
106
|
textShadow: 'none',
|
|
107
107
|
},
|
|
108
108
|
})
|
|
109
109
|
|
|
110
110
|
const primaryStyle: CSSMixinDescriptor = css({
|
|
111
111
|
background:
|
|
112
|
-
'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0) 66.46%), #101010',
|
|
112
|
+
'radial-gradient(50% 50% at 50% 0%, light-dark(rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.65)) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, light-dark(rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.35)) 0%, rgba(255, 255, 255, 0) 66.46%), light-dark(#101010, #ececec)',
|
|
113
113
|
border: 0,
|
|
114
114
|
'--rmx-button-shadow':
|
|
115
|
-
'0 16px 16px -8px rgba(0, 0, 0, 0.1), 0 8px 8px -4px rgba(0, 0, 0, 0.1), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px #101010, inset 0 0 4px 2px rgba(255, 255, 255, 0.1), inset 0 0 12px -6px rgba(255, 255, 255, 0.75)',
|
|
116
|
-
color: '#FFFFFF',
|
|
117
|
-
textShadow: '0 1px 1px #000000',
|
|
115
|
+
'0 16px 16px -8px rgba(0, 0, 0, 0.1), 0 8px 8px -4px rgba(0, 0, 0, 0.1), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px light-dark(#101010, rgba(255, 255, 255, 0.45)), inset 0 0 4px 2px rgba(255, 255, 255, 0.1), inset 0 0 12px -6px rgba(255, 255, 255, 0.75)',
|
|
116
|
+
color: 'light-dark(#FFFFFF, #151515)',
|
|
117
|
+
textShadow: '0 1px 1px light-dark(#000000, rgb(255 255 255 / 0.45))',
|
|
118
118
|
'&:hover:not(:disabled):not([aria-disabled="true"])': {
|
|
119
119
|
background:
|
|
120
|
-
'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 66.46%), #171717',
|
|
120
|
+
'radial-gradient(50% 50% at 50% 0%, light-dark(rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0.72)) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, light-dark(rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.42)) 0%, rgba(255, 255, 255, 0) 66.46%), light-dark(#171717, #ffffff)',
|
|
121
121
|
'--rmx-button-shadow':
|
|
122
|
-
'0 18px 18px -10px rgba(0, 0, 0, 0.14), 0 8px 8px -4px rgba(0, 0, 0, 0.12), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px #101010, inset 0 0 4px 2px rgba(255, 255, 255, 0.13), inset 0 0 12px -6px rgba(255, 255, 255, 0.85)',
|
|
122
|
+
'0 18px 18px -10px rgba(0, 0, 0, 0.14), 0 8px 8px -4px rgba(0, 0, 0, 0.12), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px light-dark(#101010, rgba(255, 255, 255, 0.5)), inset 0 0 4px 2px rgba(255, 255, 255, 0.13), inset 0 0 12px -6px rgba(255, 255, 255, 0.85)',
|
|
123
123
|
},
|
|
124
124
|
'&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
|
|
125
125
|
{
|
|
126
126
|
background:
|
|
127
|
-
'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 66.46%), #080808',
|
|
127
|
+
'radial-gradient(50% 50% at 50% 0%, light-dark(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.45)) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, light-dark(rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.28)) 0%, rgba(255, 255, 255, 0) 66.46%), light-dark(#080808, #d9d9d9)',
|
|
128
128
|
'--rmx-button-shadow':
|
|
129
|
-
'0 2px 2px -1px rgba(0, 0, 0, 0.14), inset 0 0 4px 2px #050505, inset 0 1px 2px rgba(0, 0, 0, 0.45), inset 0 0 10px -6px rgba(255, 255, 255, 0.55)',
|
|
130
|
-
textShadow: '0 1px 1px #000000',
|
|
129
|
+
'0 2px 2px -1px rgba(0, 0, 0, 0.14), inset 0 0 4px 2px light-dark(#050505, rgba(255, 255, 255, 0.35)), inset 0 1px 2px rgba(0, 0, 0, 0.45), inset 0 0 10px -6px rgba(255, 255, 255, 0.55)',
|
|
130
|
+
textShadow: '0 1px 1px light-dark(#000000, rgb(255 255 255 / 0.35))',
|
|
131
131
|
},
|
|
132
132
|
'&:active:not(:disabled):not([aria-disabled="true"])': {
|
|
133
133
|
transform: 'translateY(1px)',
|
|
@@ -138,14 +138,14 @@ const ghostStyle: CSSMixinDescriptor = css({
|
|
|
138
138
|
background: 'transparent',
|
|
139
139
|
border: '1px solid transparent',
|
|
140
140
|
'--rmx-button-shadow': '0 0 0 0 rgba(0, 0, 0, 0)',
|
|
141
|
-
color: '#101010',
|
|
141
|
+
color: 'light-dark(#101010, #ececec)',
|
|
142
142
|
textShadow: 'none',
|
|
143
143
|
'&:hover:not(:disabled):not([aria-disabled="true"])': {
|
|
144
|
-
background: 'rgba(16, 16, 16, 0.05)',
|
|
144
|
+
background: 'light-dark(rgba(16, 16, 16, 0.05), rgba(236, 236, 236, 0.1))',
|
|
145
145
|
},
|
|
146
146
|
'&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
|
|
147
147
|
{
|
|
148
|
-
background: 'rgba(16, 16, 16, 0.08)',
|
|
148
|
+
background: 'light-dark(rgba(16, 16, 16, 0.08), rgba(236, 236, 236, 0.14))',
|
|
149
149
|
},
|
|
150
150
|
})
|
|
151
151
|
|
package/src/checkbox/index.ts
CHANGED
|
@@ -29,8 +29,8 @@ const checkedActiveSelector =
|
|
|
29
29
|
'&:checked:active:not(:disabled):not([aria-disabled="true"]), &[aria-checked="true"]:active:not(:disabled):not([aria-disabled="true"]), &[data-state="checked"]:active:not(:disabled):not([aria-disabled="true"])'
|
|
30
30
|
const mixedActiveSelector =
|
|
31
31
|
'&:indeterminate:active:not(:disabled):not([aria-disabled="true"]), &[indeterminate]:active:not(:disabled):not([aria-disabled="true"]), &[aria-checked="mixed"]:active:not(:disabled):not([aria-disabled="true"]), &[data-state="mixed"]:active:not(:disabled):not([aria-disabled="true"])'
|
|
32
|
-
const
|
|
33
|
-
"url(\"data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%
|
|
32
|
+
const checkIconMask =
|
|
33
|
+
"url(\"data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.75 5.76562L5.10156 8.25L9.23438 1.75' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")"
|
|
34
34
|
|
|
35
35
|
const checkboxAriaChecked = {
|
|
36
36
|
checked: 'true',
|
|
@@ -75,10 +75,10 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
75
75
|
padding: 0,
|
|
76
76
|
border: 0,
|
|
77
77
|
borderRadius: 'var(--rmx-checkbox-radius)',
|
|
78
|
-
background: '#FFFFFF',
|
|
78
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
79
79
|
boxShadow:
|
|
80
|
-
'0 2px 2px -1px rgba(0, 0, 0, 0.05), 0 3px 4px -1.5px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 5px 16px -2.5px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.12)',
|
|
81
|
-
color: '#FFFFFF',
|
|
80
|
+
'0 2px 2px -1px rgba(0, 0, 0, 0.05), 0 3px 4px -1.5px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 5px 16px -2.5px rgba(0, 0, 0, 0.05), 0 0 0 1px light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))',
|
|
81
|
+
color: 'light-dark(#FFFFFF, #151515)',
|
|
82
82
|
verticalAlign: 'middle',
|
|
83
83
|
flex: 'none',
|
|
84
84
|
'&::before': {
|
|
@@ -92,7 +92,7 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
92
92
|
},
|
|
93
93
|
[`${checkedSelector}, ${mixedSelector}`]: {
|
|
94
94
|
background:
|
|
95
|
-
'linear-gradient(180deg, rgba(0, 0, 0, 0) 24.52%, rgba(0, 0, 0, 0.1) 100%), #3573F6',
|
|
95
|
+
'linear-gradient(180deg, rgba(0, 0, 0, 0) 24.52%, light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.14)) 100%), light-dark(#3573F6, #6eaaff)',
|
|
96
96
|
backgroundBlendMode: 'overlay, normal',
|
|
97
97
|
borderRadius: 'var(--rmx-checkbox-active-radius)',
|
|
98
98
|
boxShadow:
|
|
@@ -104,7 +104,9 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
104
104
|
top: '50%',
|
|
105
105
|
width: 'var(--rmx-checkbox-check-size)',
|
|
106
106
|
height: 'var(--rmx-checkbox-check-size)',
|
|
107
|
-
background:
|
|
107
|
+
background: 'currentColor',
|
|
108
|
+
mask: `${checkIconMask} center / contain no-repeat`,
|
|
109
|
+
WebkitMask: `${checkIconMask} center / contain no-repeat`,
|
|
108
110
|
transform: 'translate(-50%, calc(-50% + var(--rmx-checkbox-check-y)))',
|
|
109
111
|
},
|
|
110
112
|
[mixedMarkSelector]: {
|
|
@@ -121,7 +123,7 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
121
123
|
},
|
|
122
124
|
[`&:active${enabledSelector}`]: {
|
|
123
125
|
boxShadow:
|
|
124
|
-
'0 1px 1px -0.5px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.14), inset 0 1px 2px rgba(0, 0, 0, 0.08)',
|
|
126
|
+
'0 1px 1px -0.5px rgba(0, 0, 0, 0.06), 0 0 0 1px light-dark(rgba(0, 0, 0, 0.14), rgba(255, 255, 255, 0.24)), inset 0 1px 2px rgba(0, 0, 0, 0.08)',
|
|
125
127
|
},
|
|
126
128
|
[`${checkedActiveSelector}, ${mixedActiveSelector}`]: {
|
|
127
129
|
boxShadow:
|
package/src/combobox/index.tsx
CHANGED
|
@@ -59,7 +59,7 @@ const comboboxInputCss: CSSMixinDescriptor = css({
|
|
|
59
59
|
fontFamily: styles.fontFamily.sans,
|
|
60
60
|
fontSize: styles.fontSize.sm,
|
|
61
61
|
lineHeight: styles.lineHeight.normal,
|
|
62
|
-
boxShadow: 'inset 0 1px 0 rgb(255 255 255 / 0.7)',
|
|
62
|
+
boxShadow: 'inset 0 1px 0 light-dark(rgb(255 255 255 / 0.7), rgb(255 255 255 / 0.08))',
|
|
63
63
|
'&:focus-visible': {
|
|
64
64
|
outline: `2px solid ${styles.colors.focus.ring}`,
|
|
65
65
|
outlineOffset: styles.space.none,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { reconcileRoots, setComponentStalenessCheck } from '../runtime/refresh.ts'
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type {
|
|
|
35
35
|
FrameHandle,
|
|
36
36
|
FrameProps,
|
|
37
37
|
} from './runtime/component.ts'
|
|
38
|
-
export type { LoadModule, ResolveFrame } from './runtime/frame.ts'
|
|
38
|
+
export type { LoadModule, ResolveFrame, ResolveFrameOptions } from './runtime/frame.ts'
|
|
39
39
|
|
|
40
40
|
// -- Elements/JSX/Props --
|
|
41
41
|
export { createElement } from './runtime/create-element.ts'
|
package/src/input/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ const frameStyles = {
|
|
|
24
24
|
'--rmx-input-root-padding-inline': '8px',
|
|
25
25
|
'--rmx-input-gap': '6px',
|
|
26
26
|
'--rmx-input-icon-size': '16px',
|
|
27
|
-
'--rmx-input-icon-color': '#707070',
|
|
27
|
+
'--rmx-input-icon-color': 'light-dark(#707070, #b3b3b3)',
|
|
28
28
|
appearance: 'none',
|
|
29
29
|
margin: 0,
|
|
30
30
|
boxSizing: 'border-box',
|
|
@@ -36,10 +36,10 @@ const frameStyles = {
|
|
|
36
36
|
paddingInline: 'var(--rmx-input-padding-inline)',
|
|
37
37
|
border: 0,
|
|
38
38
|
borderRadius: '8px',
|
|
39
|
-
background: '#FFFFFF',
|
|
39
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
40
40
|
boxShadow:
|
|
41
|
-
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.12)',
|
|
42
|
-
color: '#101010',
|
|
41
|
+
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))',
|
|
42
|
+
color: 'light-dark(#101010, #ececec)',
|
|
43
43
|
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
|
|
44
44
|
fontStyle: 'normal',
|
|
45
45
|
fontWeight: 400,
|
|
@@ -47,9 +47,9 @@ const frameStyles = {
|
|
|
47
47
|
lineHeight: '20px',
|
|
48
48
|
fontFeatureSettings: '"ss01" on, "cv01" on',
|
|
49
49
|
letterSpacing: 0,
|
|
50
|
-
textShadow: '0 1px 0 #FFFFFF',
|
|
50
|
+
textShadow: '0 1px 0 light-dark(#FFFFFF, rgb(0 0 0 / 0.35))',
|
|
51
51
|
'&::placeholder': {
|
|
52
|
-
color: '#B0B0B0',
|
|
52
|
+
color: 'light-dark(#B0B0B0, #777777)',
|
|
53
53
|
opacity: 1,
|
|
54
54
|
},
|
|
55
55
|
'&:disabled, &[aria-disabled="true"]': {
|
|
@@ -102,13 +102,13 @@ const fieldBaseStyle: CSSMixinDescriptor = css({
|
|
|
102
102
|
outline: 0,
|
|
103
103
|
background: 'transparent',
|
|
104
104
|
boxShadow: 'none',
|
|
105
|
-
color: '#101010',
|
|
105
|
+
color: 'light-dark(#101010, #ececec)',
|
|
106
106
|
font: 'inherit',
|
|
107
107
|
fontFeatureSettings: 'inherit',
|
|
108
108
|
letterSpacing: 'inherit',
|
|
109
109
|
textShadow: 'inherit',
|
|
110
110
|
'&::placeholder': {
|
|
111
|
-
color: '#B0B0B0',
|
|
111
|
+
color: 'light-dark(#B0B0B0, #777777)',
|
|
112
112
|
opacity: 1,
|
|
113
113
|
},
|
|
114
114
|
'&:disabled, &[aria-disabled="true"]': {
|
package/src/radio/index.ts
CHANGED
|
@@ -43,9 +43,9 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
43
43
|
minWidth: 'var(--rmx-radio-size)',
|
|
44
44
|
minHeight: 'var(--rmx-radio-size)',
|
|
45
45
|
padding: 0,
|
|
46
|
-
border: '1px solid rgba(0, 0, 0, 0.12)',
|
|
46
|
+
border: '1px solid light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))',
|
|
47
47
|
borderRadius: '9999px',
|
|
48
|
-
background: '#FFFFFF',
|
|
48
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
49
49
|
boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, 0.06), inset 0 2px 2px rgba(0, 0, 0, 0.06)',
|
|
50
50
|
verticalAlign: 'middle',
|
|
51
51
|
flex: 'none',
|
|
@@ -55,9 +55,9 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
55
55
|
height: 'var(--rmx-radio-mark-size)',
|
|
56
56
|
borderRadius: '99px',
|
|
57
57
|
background:
|
|
58
|
-
'linear-gradient(180deg, rgba(53, 115, 246, 0) 25%, rgba(53, 115, 246, 0.15) 100%), #FFFFFF',
|
|
58
|
+
'linear-gradient(180deg, rgba(53, 115, 246, 0) 25%, rgba(53, 115, 246, 0.15) 100%), light-dark(#FFFFFF, #f5f9ff)',
|
|
59
59
|
boxShadow:
|
|
60
|
-
'0 1px 1px -0.5px #0944BE, 0 2px 2px -1px #0944BE, 0 4px 4px -2px #0944BE, 0 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 0 2px 1px #FFFFFF',
|
|
60
|
+
'0 1px 1px -0.5px #0944BE, 0 2px 2px -1px #0944BE, 0 4px 4px -2px #0944BE, 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.28)), inset 0 0 2px 1px light-dark(#FFFFFF, rgba(255, 255, 255, 0.5))',
|
|
61
61
|
opacity: 0,
|
|
62
62
|
pointerEvents: 'none',
|
|
63
63
|
},
|
|
@@ -67,7 +67,8 @@ const baseStyle: CSSMixinDescriptor = css({
|
|
|
67
67
|
[checkedSelector]: {
|
|
68
68
|
border: 0,
|
|
69
69
|
borderRadius: '99px',
|
|
70
|
-
background:
|
|
70
|
+
background:
|
|
71
|
+
'linear-gradient(180deg, light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.16)) 0%, rgba(0, 0, 0, 0) 75.48%), light-dark(#3573F6, #6eaaff)',
|
|
71
72
|
backgroundBlendMode: 'overlay, normal',
|
|
72
73
|
boxShadow:
|
|
73
74
|
'0 1px 0 rgba(255, 255, 255, 0.6), 0 0 16px rgba(53, 115, 246, 0.25), inset 0 0 3px 1px rgba(0, 0, 0, 0.1)',
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { VirtualRoot } from './vdom.ts'
|
|
2
|
+
|
|
3
|
+
export type ClientEntryIdentity = {
|
|
4
|
+
moduleUrl: string
|
|
5
|
+
exportName: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type ClientEntryRoot = Pick<VirtualRoot, 'dispose' | 'render'>
|
|
9
|
+
|
|
10
|
+
export type ClientEntryBoundaryOwner = {
|
|
11
|
+
identity: ClientEntryIdentity
|
|
12
|
+
root: ClientEntryRoot
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CLIENT_ENTRY_BOUNDARY_OWNER = Symbol('ClientEntryBoundaryOwner')
|
|
16
|
+
|
|
17
|
+
type ClientEntryBoundaryMarker = Comment & {
|
|
18
|
+
[CLIENT_ENTRY_BOUNDARY_OWNER]?: ClientEntryBoundaryOwner
|
|
19
|
+
$rmx?: ClientEntryRoot
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getClientEntryBoundaryOwner(marker: Comment): ClientEntryBoundaryOwner | undefined {
|
|
23
|
+
return (marker as ClientEntryBoundaryMarker)[CLIENT_ENTRY_BOUNDARY_OWNER]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function setClientEntryBoundaryOwner(
|
|
27
|
+
marker: Comment,
|
|
28
|
+
identity: ClientEntryIdentity,
|
|
29
|
+
root: ClientEntryRoot,
|
|
30
|
+
): ClientEntryBoundaryOwner {
|
|
31
|
+
let owner = { identity, root }
|
|
32
|
+
Object.defineProperties(marker, {
|
|
33
|
+
[CLIENT_ENTRY_BOUNDARY_OWNER]: {
|
|
34
|
+
configurable: true,
|
|
35
|
+
value: owner,
|
|
36
|
+
},
|
|
37
|
+
$rmx: {
|
|
38
|
+
configurable: true,
|
|
39
|
+
value: root,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
return owner
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function disposeClientEntryBoundary(marker: Comment): boolean {
|
|
46
|
+
let boundaryMarker = marker as ClientEntryBoundaryMarker
|
|
47
|
+
let owner = boundaryMarker[CLIENT_ENTRY_BOUNDARY_OWNER]
|
|
48
|
+
if (!owner) return false
|
|
49
|
+
|
|
50
|
+
delete boundaryMarker[CLIENT_ENTRY_BOUNDARY_OWNER]
|
|
51
|
+
delete boundaryMarker.$rmx
|
|
52
|
+
owner.root.dispose()
|
|
53
|
+
return true
|
|
54
|
+
}
|
package/src/runtime/component.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ElementProps, ElementType, RemixNode, Renderable } from './jsx.ts'
|
|
2
|
+
import type { ElementFunction } from './element-function.ts'
|
|
3
|
+
import type { Key } from './key.ts'
|
|
2
4
|
import { TypedEventTarget } from './typed-event-target.ts'
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -138,6 +140,14 @@ export interface Context<C> {
|
|
|
138
140
|
*/
|
|
139
141
|
export type FrameContent = ReadableStream<Uint8Array> | string | RemixNode
|
|
140
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Value returned by a browser frame resolver.
|
|
145
|
+
*
|
|
146
|
+
* A response body is rendered as frame content. When `fetch()` followed a redirect, the response's
|
|
147
|
+
* final URL updates the frame source and browser URL for a top-frame navigation.
|
|
148
|
+
*/
|
|
149
|
+
export type FrameResolution = FrameContent | Response
|
|
150
|
+
|
|
141
151
|
/**
|
|
142
152
|
* Events emitted by frame handles during reloads.
|
|
143
153
|
*/
|
|
@@ -208,18 +218,26 @@ export interface BuiltinElements {
|
|
|
208
218
|
/**
|
|
209
219
|
* Key type used to stabilize host elements and components during reconciliation.
|
|
210
220
|
*/
|
|
211
|
-
export type Key
|
|
221
|
+
export type { Key } from './key.ts'
|
|
212
222
|
|
|
213
223
|
type ComponentConfig = {
|
|
214
224
|
id: string
|
|
215
|
-
type:
|
|
225
|
+
type: ElementFunction
|
|
216
226
|
frame: FrameHandle
|
|
217
|
-
getContext: (type:
|
|
227
|
+
getContext: (type: ElementFunction) => unknown
|
|
218
228
|
getFrameByName: (name: string) => FrameHandle | undefined
|
|
219
229
|
getTopFrame?: () => FrameHandle | undefined
|
|
220
230
|
signal?: AbortSignal
|
|
221
231
|
}
|
|
222
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Minimal structural view of the scheduler used for handle.update() so this
|
|
235
|
+
* module doesn't depend on the reconciler.
|
|
236
|
+
*/
|
|
237
|
+
export interface UpdateQueue {
|
|
238
|
+
enqueue(vnode: object, domParent: ParentNode): void
|
|
239
|
+
}
|
|
240
|
+
|
|
223
241
|
/**
|
|
224
242
|
* Runtime handle returned by {@link createComponent}.
|
|
225
243
|
*/
|
|
@@ -227,7 +245,7 @@ export interface ComponentHandle<C = NoContext> {
|
|
|
227
245
|
frame: FrameHandle
|
|
228
246
|
render(nextProps: ElementProps): [RemixNode, Array<() => void>]
|
|
229
247
|
remove(): Array<() => void>
|
|
230
|
-
setScheduleUpdate(
|
|
248
|
+
setScheduleUpdate(queue: UpdateQueue, vnode: object, domParent: ParentNode): void
|
|
231
249
|
getContextValue(): C | undefined
|
|
232
250
|
isRemoved(): boolean
|
|
233
251
|
}
|
|
@@ -249,12 +267,22 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
249
267
|
#connectedController: AbortController | undefined
|
|
250
268
|
#contextValue: C | undefined
|
|
251
269
|
#handle: Handle<ElementProps, C>
|
|
252
|
-
#props = {}
|
|
270
|
+
#props: ElementProps = {}
|
|
253
271
|
#renderController: AbortController | undefined
|
|
254
272
|
#renderFn: RenderFn | undefined
|
|
255
273
|
#removed = false
|
|
256
|
-
|
|
257
|
-
|
|
274
|
+
// The schedule target is stored as fields (updated each render) rather than
|
|
275
|
+
// a closure so re-renders don't allocate a new function per component.
|
|
276
|
+
#updateQueue: UpdateQueue | undefined
|
|
277
|
+
#updateVNode: object | undefined
|
|
278
|
+
#updateDomParent: ParentNode | undefined
|
|
279
|
+
#scheduleUpdate = (): void => {
|
|
280
|
+
let queue = this.#updateQueue
|
|
281
|
+
if (!queue) throw new Error('scheduleUpdate not implemented')
|
|
282
|
+
let vnode = this.#updateVNode
|
|
283
|
+
let domParent = this.#updateDomParent
|
|
284
|
+
if (!vnode || !domParent) throw new Error('scheduleUpdate target not initialized')
|
|
285
|
+
queue.enqueue(vnode, domParent)
|
|
258
286
|
}
|
|
259
287
|
#tasks: Task[] = []
|
|
260
288
|
|
|
@@ -276,14 +304,15 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
276
304
|
let renderFn = this.#renderFn
|
|
277
305
|
|
|
278
306
|
if (renderFn === undefined) {
|
|
279
|
-
let
|
|
307
|
+
let initialize = this.#config.type as unknown as (handle: Handle<ElementProps, C>) => unknown
|
|
308
|
+
let result = initialize(this.#handle)
|
|
280
309
|
|
|
281
|
-
if (
|
|
310
|
+
if (!isRenderFn(result)) {
|
|
282
311
|
let name = this.#config.type.name || 'Anonymous'
|
|
283
312
|
throw new Error(`${name} must return a render function, received ${typeof result}`)
|
|
284
313
|
}
|
|
285
314
|
|
|
286
|
-
renderFn = result
|
|
315
|
+
renderFn = result
|
|
287
316
|
this.#renderFn = renderFn
|
|
288
317
|
}
|
|
289
318
|
|
|
@@ -291,15 +320,18 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
291
320
|
}
|
|
292
321
|
|
|
293
322
|
remove = (): Array<() => void> => {
|
|
294
|
-
if (this.#removed) return
|
|
323
|
+
if (this.#removed) return EMPTY_TASKS
|
|
295
324
|
this.#removed = true
|
|
296
325
|
this.#connectedController?.abort()
|
|
297
326
|
this.#abortRenderSignal()
|
|
298
|
-
|
|
327
|
+
if (this.#tasks.length === 0) return EMPTY_TASKS
|
|
328
|
+
return this.#dequeueTasks((sharedAbortedSignal ??= AbortSignal.abort()))
|
|
299
329
|
}
|
|
300
330
|
|
|
301
|
-
setScheduleUpdate = (
|
|
302
|
-
this.#
|
|
331
|
+
setScheduleUpdate = (queue: UpdateQueue, vnode: object, domParent: ParentNode): void => {
|
|
332
|
+
this.#updateQueue = queue
|
|
333
|
+
this.#updateVNode = vnode
|
|
334
|
+
this.#updateDomParent = domParent
|
|
303
335
|
}
|
|
304
336
|
|
|
305
337
|
getContextValue = (): C | undefined => this.#contextValue
|
|
@@ -312,7 +344,8 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
312
344
|
set: (value: C) => {
|
|
313
345
|
this.#contextValue = value
|
|
314
346
|
},
|
|
315
|
-
get: (type: ElementType | symbol) =>
|
|
347
|
+
get: (type: ElementType | symbol) =>
|
|
348
|
+
isElementFunction(type) ? this.#config.getContext(type) : undefined,
|
|
316
349
|
}
|
|
317
350
|
|
|
318
351
|
return {
|
|
@@ -358,6 +391,8 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
358
391
|
}
|
|
359
392
|
|
|
360
393
|
#dequeueTasks(signal?: AbortSignal): Array<() => void> {
|
|
394
|
+
if (this.#tasks.length === 0) return EMPTY_TASKS
|
|
395
|
+
|
|
361
396
|
let needsSignal = signal === undefined && this.#tasks.some((task) => task.length >= 1)
|
|
362
397
|
|
|
363
398
|
if (needsSignal) {
|
|
@@ -365,11 +400,25 @@ class ComponentRuntime<C = NoContext> implements ComponentHandle<C> {
|
|
|
365
400
|
}
|
|
366
401
|
|
|
367
402
|
signal ??= this.#renderController?.signal
|
|
403
|
+
signal ??= sharedAbortedSignal ??= AbortSignal.abort()
|
|
368
404
|
let tasks = this.#tasks.splice(0, this.#tasks.length)
|
|
369
|
-
return tasks.map((task) => () => task(signal
|
|
405
|
+
return tasks.map((task) => () => task(signal))
|
|
370
406
|
}
|
|
371
407
|
}
|
|
372
408
|
|
|
409
|
+
function isRenderFn(value: unknown): value is RenderFn {
|
|
410
|
+
return typeof value === 'function'
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function isElementFunction(value: unknown): value is ElementFunction {
|
|
414
|
+
return typeof value === 'function'
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Shared empty result and aborted signal so the common no-task removal path
|
|
418
|
+
// (large subtree teardowns dispose many components at once) allocates nothing.
|
|
419
|
+
const EMPTY_TASKS: Array<() => void> = []
|
|
420
|
+
let sharedAbortedSignal: AbortSignal | undefined
|
|
421
|
+
|
|
373
422
|
function syncProps(target: ElementProps, next: ElementProps): void {
|
|
374
423
|
for (let key in target) {
|
|
375
424
|
if (!(key in next)) {
|
|
@@ -62,10 +62,31 @@ export function canUseProperty(
|
|
|
62
62
|
return name in element
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// Prop names repeat constantly across elements and renders; cache the
|
|
66
|
+
// normalized results so the string work and object allocation happen once
|
|
67
|
+
// per distinct name. Entries are shared — callers must not mutate them.
|
|
68
|
+
const NORMALIZATION_CACHE_LIMIT = 256
|
|
69
|
+
const htmlAttributeNameCache = new Map<string, { ns?: string; attr: string }>()
|
|
70
|
+
const svgAttributeNameCache = new Map<string, { ns?: string; attr: string }>()
|
|
71
|
+
|
|
65
72
|
export function normalizeAttributeName(
|
|
66
73
|
name: string,
|
|
67
74
|
isSvg: boolean,
|
|
68
75
|
): { ns?: string; attr: string } {
|
|
76
|
+
let cache = isSvg ? svgAttributeNameCache : htmlAttributeNameCache
|
|
77
|
+
let cached = cache.get(name)
|
|
78
|
+
if (cached === undefined) {
|
|
79
|
+
cached = computeAttributeName(name, isSvg)
|
|
80
|
+
if (cache.size >= NORMALIZATION_CACHE_LIMIT) {
|
|
81
|
+
let oldest = cache.keys().next()
|
|
82
|
+
if (!oldest.done) cache.delete(oldest.value)
|
|
83
|
+
}
|
|
84
|
+
cache.set(name, cached)
|
|
85
|
+
}
|
|
86
|
+
return cached
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function computeAttributeName(name: string, isSvg: boolean): { ns?: string; attr: string } {
|
|
69
90
|
if (name.startsWith('aria-') || name.startsWith('data-')) return { attr: name }
|
|
70
91
|
|
|
71
92
|
if (name === 'className') return { attr: 'class' }
|
|
@@ -113,6 +134,19 @@ export function getMergedClassName(props: ElementProps): string | undefined {
|
|
|
113
134
|
return merged || undefined
|
|
114
135
|
}
|
|
115
136
|
|
|
137
|
+
// Style/attribute names come from a small recurring set, so cache conversions
|
|
138
|
+
// instead of running a regex per property per element per render.
|
|
139
|
+
const kebabCaseCache = new Map<string, string>()
|
|
140
|
+
|
|
116
141
|
export function toKebabCase(value: string): string {
|
|
117
|
-
|
|
142
|
+
let cached = kebabCaseCache.get(value)
|
|
143
|
+
if (cached === undefined) {
|
|
144
|
+
cached = value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`)
|
|
145
|
+
if (kebabCaseCache.size >= NORMALIZATION_CACHE_LIMIT) {
|
|
146
|
+
let oldest = kebabCaseCache.keys().next()
|
|
147
|
+
if (!oldest.done) kebabCaseCache.delete(oldest.value)
|
|
148
|
+
}
|
|
149
|
+
kebabCaseCache.set(value, cached)
|
|
150
|
+
}
|
|
151
|
+
return cached
|
|
118
152
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { RemixNode } from '../jsx.ts'
|
|
2
|
+
import { invariant } from '../invariant.ts'
|
|
3
|
+
import { isRemixElement } from './vnode.ts'
|
|
2
4
|
|
|
3
5
|
export type EmptyChild = boolean | null | undefined
|
|
4
6
|
export type PrimitiveChild = string | number | bigint
|
|
@@ -13,8 +15,8 @@ export function isPrimitiveChild(value: RemixNode): value is PrimitiveChild {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function normalizeChildren(children: readonly RemixNode[]): RemixNode[] {
|
|
16
|
-
for (let
|
|
17
|
-
if (Array.isArray(
|
|
18
|
+
for (let i = 0; i < children.length; i++) {
|
|
19
|
+
if (Array.isArray(children[i])) {
|
|
18
20
|
return (children as unknown[]).flat(Infinity) as RemixNode[]
|
|
19
21
|
}
|
|
20
22
|
}
|
|
@@ -22,6 +24,26 @@ export function normalizeChildren(children: readonly RemixNode[]): RemixNode[] {
|
|
|
22
24
|
return children as RemixNode[]
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
export function assertRemixNodes(values: readonly unknown[]): asserts values is RemixNode[] {
|
|
28
|
+
for (let value of values) {
|
|
29
|
+
invariant(isRemixNode(value), 'Invalid child node')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isRemixNode(value: unknown): value is RemixNode {
|
|
34
|
+
if (value == null) return true
|
|
35
|
+
let type = typeof value
|
|
36
|
+
if (type === 'string' || type === 'number' || type === 'bigint' || type === 'boolean') {
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
if (isRemixElement(value)) return true
|
|
40
|
+
if (!Array.isArray(value)) return false
|
|
41
|
+
for (let child of value) {
|
|
42
|
+
if (!isRemixNode(child)) return false
|
|
43
|
+
}
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
export function packChildren(children: readonly RemixNode[]): RemixNode | undefined {
|
|
26
48
|
if (children.length === 0) {
|
|
27
49
|
return undefined
|