@shortfuse/materialdesignweb 0.7.6 → 0.8.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 +57 -68
- package/components/Badge.js +2 -2
- package/components/BottomAppBar.js +3 -5
- package/components/Box.js +33 -3
- package/components/Button.js +48 -21
- package/components/Button.md +9 -9
- package/components/Card.js +9 -16
- package/components/Checkbox.js +45 -36
- package/components/CheckboxIcon.js +2 -2
- package/components/Chip.js +1 -1
- package/components/Dialog.js +228 -359
- package/components/DialogActions.js +2 -2
- package/components/Divider.js +3 -3
- package/components/ExtendedFab.js +4 -8
- package/components/Fab.js +1 -2
- package/components/FilterChip.js +4 -4
- package/components/Headline.js +1 -1
- package/components/Icon.js +8 -8
- package/components/IconButton.js +9 -14
- package/components/Input.js +273 -1
- package/components/Layout.js +485 -16
- package/components/List.js +6 -4
- package/components/ListItem.js +12 -12
- package/components/ListOption.js +21 -5
- package/components/Listbox.js +239 -0
- package/components/Menu.js +77 -526
- package/components/MenuItem.js +12 -14
- package/components/Nav.js +0 -2
- package/components/NavBar.js +8 -79
- package/components/NavDrawer.js +12 -11
- package/components/NavDrawerItem.js +2 -1
- package/components/NavItem.js +18 -8
- package/components/NavRail.js +15 -7
- package/components/NavRailItem.js +3 -1
- package/components/Popup.js +20 -0
- package/components/Progress.js +24 -23
- package/components/Radio.js +42 -35
- package/components/RadioIcon.js +3 -3
- package/components/Ripple.js +2 -3
- package/components/Search.js +85 -0
- package/components/SegmentedButton.js +1 -10
- package/components/SegmentedButtonGroup.js +16 -10
- package/components/Select.js +4 -4
- package/components/Shape.js +1 -1
- package/components/Slider.js +43 -50
- package/components/Snackbar.js +4 -5
- package/components/Surface.js +3 -3
- package/components/Switch.js +55 -21
- package/components/SwitchIcon.js +10 -8
- package/components/Tab.js +11 -9
- package/components/TabContent.js +4 -3
- package/components/TabList.js +2 -2
- package/components/TabPanel.js +11 -8
- package/components/TextArea.js +38 -35
- package/components/Tooltip.js +2 -2
- package/components/TopAppBar.js +65 -147
- package/core/Composition.js +985 -628
- package/core/CompositionAdapter.js +315 -0
- package/core/CustomElement.js +153 -90
- package/core/DomAdapter.js +586 -0
- package/core/ICustomElement.d.ts +2 -2
- package/core/css.js +8 -7
- package/core/customTypes.js +53 -31
- package/{utils → core}/jsonMergePatch.js +36 -14
- package/core/observe.js +111 -57
- package/core/optimizations.js +23 -0
- package/core/template.js +17 -11
- package/core/test.js +126 -0
- package/core/typings.d.ts +11 -5
- package/core/uid.js +13 -0
- package/dist/index.min.js +83 -152
- package/dist/index.min.js.map +4 -4
- package/dist/meta.json +1 -1
- package/mixins/AriaReflectorMixin.js +1 -2
- package/mixins/AriaToolbarMixin.js +2 -3
- package/mixins/ControlMixin.js +25 -17
- package/mixins/DensityMixin.js +0 -1
- package/mixins/FlexableMixin.js +1 -2
- package/mixins/FormAssociatedMixin.js +13 -10
- package/mixins/InputMixin.js +2 -9
- package/mixins/KeyboardNavMixin.js +14 -1
- package/mixins/PopupMixin.js +757 -0
- package/mixins/RTLObserverMixin.js +0 -1
- package/mixins/ResizeObserverMixin.js +0 -1
- package/mixins/RippleMixin.js +3 -4
- package/mixins/ScrollListenerMixin.js +41 -32
- package/mixins/SemiStickyMixin.js +151 -0
- package/mixins/ShapeMixin.js +29 -24
- package/mixins/StateMixin.js +11 -6
- package/mixins/SurfaceMixin.js +3 -57
- package/mixins/TextFieldMixin.js +57 -65
- package/mixins/ThemableMixin.js +78 -156
- package/mixins/TooltipTriggerMixin.js +7 -13
- package/mixins/TouchTargetMixin.js +4 -3
- package/package.json +9 -5
- package/theming/index.js +1 -1
- package/theming/themableMixinLoader.js +12 -0
- package/utils/{hct → material-color}/blend.js +8 -10
- package/utils/{hct → material-color/hct}/Cam16.js +196 -69
- package/utils/{hct → material-color/hct}/Hct.js +61 -19
- package/utils/{hct → material-color/hct}/ViewingConditions.js +3 -3
- package/utils/{hct → material-color/hct}/hctSolver.js +9 -16
- package/utils/{hct → material-color}/helper.js +11 -18
- package/utils/{hct → material-color/palettes}/CorePalette.js +79 -19
- package/utils/{hct → material-color/palettes}/TonalPalette.js +12 -4
- package/utils/material-color/scheme/Scheme.js +376 -0
- package/utils/{hct/colorUtils.js → material-color/utils/color.js} +61 -1
- package/utils/popup.js +46 -25
- package/components/ListSelect.js +0 -220
- package/components/Option.js +0 -91
- package/components/Pane.js +0 -281
- package/core/identify.js +0 -40
- package/utils/hct/Scheme.js +0 -587
- /package/utils/{hct/mathUtils.js → material-color/utils/math.js} +0 -0
package/core/customTypes.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/** @typedef {import('./CustomElement').default} CustomElement */
|
|
2
2
|
|
|
3
|
-
/** @type {WeakMap<HTMLElement, EventListener>} */
|
|
4
|
-
const eventHandlerValues = new Map();
|
|
5
|
-
|
|
6
3
|
/**
|
|
7
4
|
* @see https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes
|
|
8
5
|
* @type {import('./typings.js').ObserverOptions<'function',EventListener, unknown>}
|
|
@@ -11,11 +8,10 @@ export const EVENT_HANDLER_TYPE = {
|
|
|
11
8
|
type: 'function',
|
|
12
9
|
reflect: 'read',
|
|
13
10
|
value: null,
|
|
14
|
-
values: eventHandlerValues,
|
|
15
11
|
parser(v) { return v; },
|
|
16
12
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
17
13
|
if (oldValue == null && newValue == null) return;
|
|
18
|
-
// Must continue if oldValue === newValue;
|
|
14
|
+
// Must continue even if oldValue === newValue;
|
|
19
15
|
if (newValue == null) {
|
|
20
16
|
this[name] = null;
|
|
21
17
|
return;
|
|
@@ -49,39 +45,51 @@ export const WEAKREF_TYPE = {
|
|
|
49
45
|
type: 'object',
|
|
50
46
|
reflect: false,
|
|
51
47
|
value: null,
|
|
52
|
-
values: weakRefValues,
|
|
53
48
|
parser(v) { return new WeakRef(v); },
|
|
54
49
|
get() {
|
|
55
|
-
|
|
50
|
+
if (weakRefValues.has(this)) {
|
|
51
|
+
return weakRefValues.get(this).deref();
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
56
54
|
},
|
|
57
55
|
};
|
|
58
56
|
|
|
59
|
-
/** @type {WeakMap<any, Animation>} */
|
|
60
|
-
const elementStylerLastAnimation = new WeakMap();
|
|
61
|
-
/** @type {WeakMap<CustomElement, ElementStylerOptions>} */
|
|
62
|
-
const elementStylerValues = new WeakMap();
|
|
63
|
-
/** @type {WeakSet<any>} */
|
|
64
|
-
const elementStylerHasQueue = new WeakSet();
|
|
65
|
-
|
|
66
57
|
/**
|
|
67
58
|
* @typedef {Object} ElementStylerOptions
|
|
68
|
-
* @prop {string} target
|
|
59
|
+
* @prop {string|HTMLElement|null} target tag, element or null (host)
|
|
69
60
|
* @prop {Keyframe} styles
|
|
70
61
|
* @prop {EffectTiming} [timing]
|
|
71
62
|
*/
|
|
72
63
|
|
|
73
|
-
/** @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
/** @type {WeakMap<CustomElement, Set<string>} */
|
|
65
|
+
const queuedPropsByElement = new WeakMap();
|
|
66
|
+
|
|
67
|
+
/** @type {WeakMap<CustomElement, Map<string, Animation>>} */
|
|
68
|
+
const previousAnimationsByElement = new WeakMap();
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} name
|
|
72
|
+
* @this {CustomElement}
|
|
73
|
+
*/
|
|
74
|
+
function elementStylerMicrotaskCallback(name) {
|
|
75
|
+
const previousAnimations = previousAnimationsByElement.get(this);
|
|
76
|
+
/** @type {Animation} */
|
|
77
|
+
let previousAnimation;
|
|
78
|
+
if (previousAnimations?.has(name)) {
|
|
79
|
+
previousAnimation = previousAnimations.get(name);
|
|
80
|
+
}
|
|
81
|
+
const value = this[name];
|
|
77
82
|
if (!value) {
|
|
78
83
|
previousAnimation?.cancel();
|
|
79
84
|
return;
|
|
80
85
|
}
|
|
86
|
+
const { target, styles, timing } = value;
|
|
81
87
|
/** @type {HTMLElement} */
|
|
82
|
-
const el =
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
const el = target
|
|
89
|
+
? (typeof target === 'string' ? this.refs[target] : target)
|
|
90
|
+
: this;
|
|
91
|
+
const currentAnimation = el.animate(styles, {
|
|
92
|
+
...timing,
|
|
85
93
|
fill: 'forwards',
|
|
86
94
|
});
|
|
87
95
|
currentAnimation.onremove = () => {
|
|
@@ -93,22 +101,32 @@ function elementStylerRAFCallback() {
|
|
|
93
101
|
previousAnimation?.cancel();
|
|
94
102
|
previousAnimation = null;
|
|
95
103
|
};
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
if (previousAnimations) {
|
|
105
|
+
previousAnimations.set(name, currentAnimation);
|
|
106
|
+
} else {
|
|
107
|
+
previousAnimationsByElement.set(this, new Map([[name, currentAnimation]]));
|
|
108
|
+
}
|
|
109
|
+
queuedPropsByElement.get(this).delete(name);
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
/** @type {import('./typings.js').ObserverOptions<'object',ElementStylerOptions, CustomElement>} */
|
|
101
113
|
export const ELEMENT_STYLER_TYPE = {
|
|
102
114
|
type: 'object',
|
|
103
115
|
reflect: false,
|
|
104
|
-
values: elementStylerValues,
|
|
105
116
|
diff: null, // Skip computing entire change
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
propChangedCallback(name, oldValue, newValue) {
|
|
118
|
+
if (!this.isConnected) return;
|
|
119
|
+
const queuedProps = queuedPropsByElement.get(this);
|
|
120
|
+
let hasQueue = false;
|
|
121
|
+
if (queuedProps?.has(name)) {
|
|
122
|
+
hasQueue = true;
|
|
123
|
+
}
|
|
108
124
|
if (!newValue) {
|
|
109
125
|
if (!hasQueue) return;
|
|
110
|
-
console.warn('debug
|
|
111
|
-
|
|
126
|
+
console.warn('debug of cancel needed');
|
|
127
|
+
if (queuedProps) {
|
|
128
|
+
queuedProps.delete(name);
|
|
129
|
+
}
|
|
112
130
|
return;
|
|
113
131
|
}
|
|
114
132
|
|
|
@@ -117,9 +135,13 @@ export const ELEMENT_STYLER_TYPE = {
|
|
|
117
135
|
return;
|
|
118
136
|
}
|
|
119
137
|
|
|
138
|
+
if (queuedProps) {
|
|
139
|
+
queuedProps.add(name);
|
|
140
|
+
} else {
|
|
141
|
+
queuedPropsByElement.set(this, new Set([name]));
|
|
142
|
+
}
|
|
120
143
|
// Animation styles may trickle in steps, so queue a microtask before doing any work.
|
|
121
144
|
// Using requestAnimationFrame would fire one frame too late for CSS animations already scheduled
|
|
122
|
-
queueMicrotask(
|
|
123
|
-
elementStylerHasQueue.add(this);
|
|
145
|
+
queueMicrotask(elementStylerMicrotaskCallback.bind(this, name));
|
|
124
146
|
},
|
|
125
147
|
};
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export function applyMergePatch(target, patch) {
|
|
11
11
|
if (target === patch) return target;
|
|
12
|
-
if (patch == null || typeof patch !== 'object') return patch;
|
|
13
|
-
if (
|
|
12
|
+
if (target == null || patch == null || typeof patch !== 'object') return patch;
|
|
13
|
+
if (typeof target !== 'object') {
|
|
14
14
|
target = {};
|
|
15
15
|
}
|
|
16
16
|
for (const [key, value] of Object.entries(patch)) {
|
|
@@ -28,26 +28,52 @@ export function applyMergePatch(target, patch) {
|
|
|
28
28
|
/**
|
|
29
29
|
* Creates a JSON Merge patch based
|
|
30
30
|
* Allows different strategies for arrays
|
|
31
|
-
* - `
|
|
31
|
+
* - `reference`: Per spec, returns array as is
|
|
32
|
+
* - `clone`: Clones all entries with no inspection.
|
|
32
33
|
* - `object`: Convert to flattened, array-like objects. Requires
|
|
33
34
|
* consumer of patch to be aware of the schema beforehand.
|
|
34
35
|
* @param {object|number|string|boolean} previous
|
|
35
36
|
* @param {object|number|string|boolean} current
|
|
36
|
-
* @param {'clone'|'object'} [arrayStrategy='
|
|
37
|
+
* @param {'clone'|'object'|'reference'} [arrayStrategy='reference']
|
|
37
38
|
* @return {any} Patch
|
|
38
39
|
*/
|
|
39
|
-
export function buildMergePatch(previous, current, arrayStrategy = '
|
|
40
|
+
export function buildMergePatch(previous, current, arrayStrategy = 'reference') {
|
|
40
41
|
if (previous === current) return null;
|
|
41
42
|
if (current == null || typeof current !== 'object') return current;
|
|
42
43
|
if (previous == null || typeof previous !== 'object') {
|
|
43
44
|
return structuredClone(current);
|
|
44
45
|
}
|
|
45
|
-
const isArray = Array.isArray(current);
|
|
46
|
-
if (isArray && arrayStrategy === 'clone') {
|
|
47
|
-
return structuredClone(current);
|
|
48
|
-
}
|
|
49
46
|
|
|
50
47
|
const patch = {};
|
|
48
|
+
if (Array.isArray(current)) {
|
|
49
|
+
if (arrayStrategy === 'reference') {
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
52
|
+
// Assume previous is array
|
|
53
|
+
if (arrayStrategy === 'clone') {
|
|
54
|
+
return structuredClone(current);
|
|
55
|
+
}
|
|
56
|
+
for (const [index, value] of current.entries()) {
|
|
57
|
+
if (value == null) {
|
|
58
|
+
console.warn('Nullish value found at', index);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const changes = buildMergePatch(previous[index], value, arrayStrategy);
|
|
62
|
+
if (changes === null) {
|
|
63
|
+
continue;
|
|
64
|
+
} else {
|
|
65
|
+
patch[index] = changes;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// for (let i = current.length; i < previous.length; i++) {
|
|
69
|
+
// patch[i] = null;
|
|
70
|
+
// }
|
|
71
|
+
if (current.length !== previous.length) {
|
|
72
|
+
patch.length = current.length;
|
|
73
|
+
}
|
|
74
|
+
return patch;
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
const previousKeys = new Set(Object.keys(previous));
|
|
52
78
|
for (const [key, value] of Object.entries(current)) {
|
|
53
79
|
previousKeys.delete(key);
|
|
@@ -57,7 +83,7 @@ export function buildMergePatch(previous, current, arrayStrategy = 'clone') {
|
|
|
57
83
|
}
|
|
58
84
|
const changes = buildMergePatch(previous[key], value, arrayStrategy);
|
|
59
85
|
if (changes === null) {
|
|
60
|
-
console.log('keeping', key);
|
|
86
|
+
// console.log('keeping', key);
|
|
61
87
|
} else {
|
|
62
88
|
patch[key] = changes;
|
|
63
89
|
}
|
|
@@ -67,10 +93,6 @@ export function buildMergePatch(previous, current, arrayStrategy = 'clone') {
|
|
|
67
93
|
console.log('removing', key);
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
if (isArray && arrayStrategy === 'object' && current.length !== previous.length) {
|
|
71
|
-
patch.length = current.length;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
96
|
return patch;
|
|
75
97
|
}
|
|
76
98
|
|
package/core/observe.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { buildMergePatch, hasMergePatch } from '../utils/jsonMergePatch.js';
|
|
2
|
-
|
|
3
1
|
import { attrNameFromPropName } from './dom.js';
|
|
2
|
+
import { buildMergePatch, hasMergePatch } from './jsonMergePatch.js';
|
|
4
3
|
|
|
5
4
|
/** @typedef {import('./typings.js').ObserverPropertyType} ObserverPropertyType */
|
|
6
5
|
|
|
@@ -43,15 +42,24 @@ const DEFAULT_OBJECT_PARSER = (o) => o;
|
|
|
43
42
|
* @param {T} b
|
|
44
43
|
* @return {boolean} true if equal
|
|
45
44
|
*/
|
|
46
|
-
|
|
45
|
+
const DEFAULT_OBJECT_COMPARATOR = (a, b) => !hasMergePatch(a, b);
|
|
47
46
|
|
|
48
47
|
/**
|
|
48
|
+
* Always invoke change on set
|
|
49
49
|
* @template T
|
|
50
50
|
* @param {T} a
|
|
51
51
|
* @param {T} b
|
|
52
52
|
* @return {boolean} true if equal
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
const DEFAULT_ARRAY_COMPARATOR = (a, b) => false;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @template T
|
|
58
|
+
* @param {T} a
|
|
59
|
+
* @param {T} b
|
|
60
|
+
* @return {boolean} true if equal
|
|
61
|
+
*/
|
|
62
|
+
const DEFAULT_OBJECT_DIFF = (a, b) => buildMergePatch(a, b, 'reference');
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
* @param {ObserverPropertyType} type
|
|
@@ -78,6 +86,48 @@ function emptyFromType(type) {
|
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @template {Object} T
|
|
91
|
+
* @param {T} proxyTarget
|
|
92
|
+
* @param {Set<string>} set
|
|
93
|
+
* @param {Set<string>} deepSet
|
|
94
|
+
* @param {string} [prefix]
|
|
95
|
+
* @return {T}
|
|
96
|
+
*/
|
|
97
|
+
function buildProxy(proxyTarget, set, deepSet, prefix) {
|
|
98
|
+
proxyTarget ??= {};
|
|
99
|
+
return new Proxy(proxyTarget, {
|
|
100
|
+
get(target, p) {
|
|
101
|
+
const value = target[p];
|
|
102
|
+
if (typeof p !== 'symbol') {
|
|
103
|
+
const arg = prefix ? `${prefix}.${p}` : p;
|
|
104
|
+
if (prefix) {
|
|
105
|
+
deepSet.add(arg);
|
|
106
|
+
} else {
|
|
107
|
+
set.add(arg);
|
|
108
|
+
}
|
|
109
|
+
if (typeof value === 'object' && value != null) {
|
|
110
|
+
console.debug('tried to arg poke object get', p, value);
|
|
111
|
+
return buildProxy(value, set, deepSet, arg);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
},
|
|
116
|
+
has(target, p) {
|
|
117
|
+
const value = Reflect.has(target, p);
|
|
118
|
+
if (typeof p !== 'symbol') {
|
|
119
|
+
const arg = prefix ? `${prefix}.p` : p;
|
|
120
|
+
if (prefix) {
|
|
121
|
+
deepSet.add(arg);
|
|
122
|
+
} else {
|
|
123
|
+
set.add(arg);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return value;
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
81
131
|
/**
|
|
82
132
|
* @param {ObserverPropertyType} type
|
|
83
133
|
* @return {any}
|
|
@@ -98,7 +148,7 @@ function defaultParserFromType(type) {
|
|
|
98
148
|
case 'object':
|
|
99
149
|
return DEFAULT_OBJECT_PARSER;
|
|
100
150
|
case 'array':
|
|
101
|
-
return
|
|
151
|
+
return DEFAULT_OBJECT_PARSER;
|
|
102
152
|
default:
|
|
103
153
|
case 'string':
|
|
104
154
|
return DEFAULT_STRING_PARSER;
|
|
@@ -166,7 +216,7 @@ export function parseObserverOptions(name, typeOrOptions, object) {
|
|
|
166
216
|
if (!isFn) {
|
|
167
217
|
isFn = parsedType === 'object'
|
|
168
218
|
? DEFAULT_OBJECT_COMPARATOR
|
|
169
|
-
: Object.is;
|
|
219
|
+
: ((parsedType === 'array') ? DEFAULT_ARRAY_COMPARATOR : Object.is);
|
|
170
220
|
}
|
|
171
221
|
|
|
172
222
|
const diff = 'diff' in options
|
|
@@ -217,57 +267,56 @@ export function parsePropertyValue(value) {
|
|
|
217
267
|
|
|
218
268
|
/**
|
|
219
269
|
* @param {(data: Partial<any>) => any} fn
|
|
220
|
-
* @param {any} arg0
|
|
221
|
-
* @param {any} args[]
|
|
222
270
|
* @param {...any} args
|
|
223
271
|
* @this {any}
|
|
224
|
-
* @return {{
|
|
272
|
+
* @return {{
|
|
273
|
+
* props: {
|
|
274
|
+
* this: string[],
|
|
275
|
+
* args: string[][],
|
|
276
|
+
* },
|
|
277
|
+
* deepPropStrings: {
|
|
278
|
+
* this: string[],
|
|
279
|
+
* args: string[][],
|
|
280
|
+
* },
|
|
281
|
+
* deepProps: {
|
|
282
|
+
* this: string[][],
|
|
283
|
+
* args: string[][][],
|
|
284
|
+
* },
|
|
285
|
+
* defaultValue: any,
|
|
286
|
+
* reusable: boolean,
|
|
287
|
+
* }}
|
|
225
288
|
*/
|
|
226
|
-
export function observeFunction(fn,
|
|
227
|
-
|
|
289
|
+
export function observeFunction(fn, ...args) {
|
|
290
|
+
/** @type {Set<string>} */
|
|
228
291
|
const thisPoked = new Set();
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
set.add(arg);
|
|
242
|
-
const value = Reflect.get(target, p);
|
|
243
|
-
if (typeof value === 'object' && value != null) {
|
|
244
|
-
console.debug('tried to arg poke object get', p, value);
|
|
245
|
-
return buildProxy(value, set, arg);
|
|
246
|
-
}
|
|
247
|
-
return value;
|
|
248
|
-
},
|
|
249
|
-
has(target, p) {
|
|
250
|
-
const arg = prefix ? `${prefix}.p` : p;
|
|
251
|
-
set.add(arg);
|
|
252
|
-
const value = Reflect.has(target, p);
|
|
253
|
-
return value;
|
|
254
|
-
},
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const argProxy = buildProxy(arg0, argPoked);
|
|
259
|
-
const thisProxy = buildProxy(this ?? arg0, thisPoked);
|
|
260
|
-
const defaultValue = fn.call(thisProxy, argProxy, ...args);
|
|
292
|
+
/** @type {Set<string>} */
|
|
293
|
+
const thisPokedDeep = new Set();
|
|
294
|
+
|
|
295
|
+
const argWatchers = args.map((arg) => {
|
|
296
|
+
const poked = new Set();
|
|
297
|
+
const pokedDeep = new Set();
|
|
298
|
+
const proxy = buildProxy(arg, poked, pokedDeep);
|
|
299
|
+
return { poked, pokedDeep, proxy };
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const thisProxy = buildProxy(this ?? {}, thisPoked, thisPokedDeep);
|
|
303
|
+
const defaultValue = fn.apply(thisProxy, argWatchers.map((watcher) => watcher.proxy));
|
|
261
304
|
/* Arrow functions can reused if they don't poke `this` */
|
|
262
305
|
const reusable = fn.name ? true : !thisPoked.size;
|
|
263
306
|
|
|
264
|
-
const props = new Set([
|
|
265
|
-
...argPoked,
|
|
266
|
-
...thisPoked,
|
|
267
|
-
]);
|
|
268
|
-
|
|
269
307
|
return {
|
|
270
|
-
props
|
|
308
|
+
props: {
|
|
309
|
+
this: [...thisPoked],
|
|
310
|
+
args: argWatchers.map((watcher) => [...watcher.poked]),
|
|
311
|
+
},
|
|
312
|
+
deepPropStrings: {
|
|
313
|
+
this: [...thisPokedDeep],
|
|
314
|
+
args: argWatchers.map((watcher) => [...watcher.pokedDeep]),
|
|
315
|
+
},
|
|
316
|
+
deepProps: {
|
|
317
|
+
this: [...thisPokedDeep].map((deepPropString) => deepPropString.split('.')),
|
|
318
|
+
args: argWatchers.map((watcher) => [...watcher.pokedDeep].map((deepPropString) => deepPropString.split('.'))),
|
|
319
|
+
},
|
|
271
320
|
defaultValue,
|
|
272
321
|
reusable,
|
|
273
322
|
};
|
|
@@ -297,7 +346,6 @@ export function defineObservableProperty(object, key, options) {
|
|
|
297
346
|
* @return {boolean} changed
|
|
298
347
|
*/
|
|
299
348
|
function detectChange(oldValue, value) {
|
|
300
|
-
if (oldValue === value) return false;
|
|
301
349
|
if (config.get) {
|
|
302
350
|
// TODO: Custom getter vs parser
|
|
303
351
|
}
|
|
@@ -310,12 +358,11 @@ export function defineObservableProperty(object, key, options) {
|
|
|
310
358
|
if (oldValue == null) {
|
|
311
359
|
if (newValue == null) return false; // Both nullish
|
|
312
360
|
} else if (newValue != null) {
|
|
313
|
-
if (oldValue === newValue) return false;
|
|
361
|
+
// if (oldValue === newValue) return false;
|
|
314
362
|
if (config.diff) {
|
|
315
363
|
changes = config.diff.call(this, oldValue, newValue);
|
|
316
364
|
if (changes == null) return false;
|
|
317
|
-
}
|
|
318
|
-
if (config.is.call(this, oldValue, newValue)) return false;
|
|
365
|
+
} else if (config.is.call(this, oldValue, newValue)) return false;
|
|
319
366
|
}
|
|
320
367
|
|
|
321
368
|
config.values.set(this, newValue);
|
|
@@ -356,15 +403,22 @@ export function defineObservableProperty(object, key, options) {
|
|
|
356
403
|
if (config.get) {
|
|
357
404
|
// Custom `get` uses computed values.
|
|
358
405
|
// Invalidate computed value when dependent `prop` changes
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
...
|
|
362
|
-
|
|
406
|
+
const observeResult = observeFunction(config.get.bind(object), object, internalGet.bind(object));
|
|
407
|
+
const uniqueProps = new Set([
|
|
408
|
+
...observeResult.props.this,
|
|
409
|
+
...observeResult.props.args[0],
|
|
410
|
+
]);
|
|
411
|
+
for (const prop of uniqueProps) {
|
|
412
|
+
// @ts-ignore keyof C
|
|
413
|
+
config.watchers.push([prop, onInvalidate]);
|
|
414
|
+
}
|
|
415
|
+
|
|
363
416
|
// TODO: May be able to cache value if props are present
|
|
364
417
|
}
|
|
365
418
|
/** @type {Partial<PropertyDescriptor>} */
|
|
366
419
|
const descriptor = {
|
|
367
420
|
enumerable: config.enumerable,
|
|
421
|
+
configurable: true,
|
|
368
422
|
/**
|
|
369
423
|
* @this {C}
|
|
370
424
|
* @return {T2}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Micro-optimized functions
|
|
2
|
+
|
|
3
|
+
const BLANK_TEXT = new Text();
|
|
4
|
+
const BLANK_COMMENT = new Comment();
|
|
5
|
+
const BLANK_DIV = document.createElement('div');
|
|
6
|
+
|
|
7
|
+
/** @return {Text} */
|
|
8
|
+
export function createEmptyTextNode() {
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
return BLANK_TEXT.cloneNode();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @return {HTMLDivElement} */
|
|
14
|
+
export function createEmptyDiv() {
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
return BLANK_DIV.cloneNode();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @return {Comment} */
|
|
20
|
+
export function createEmptyComment() {
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
return BLANK_COMMENT.cloneNode();
|
|
23
|
+
}
|
package/core/template.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateUID } from './
|
|
1
|
+
import { generateUID } from './uid.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Property are bound to an ID+Node
|
|
@@ -6,7 +6,10 @@ import { generateUID } from './identify.js';
|
|
|
6
6
|
* @template {any} T
|
|
7
7
|
* @typedef {Object} InlineFunctionEntry
|
|
8
8
|
* @prop {(data:T) => any} fn
|
|
9
|
-
* @prop {
|
|
9
|
+
* @prop {string[]} [props]
|
|
10
|
+
* @prop {string[][]} [deepProps]
|
|
11
|
+
* @prop {string[]} [injectionProps]
|
|
12
|
+
* @prop {string[][]} [injectionDeepProps]
|
|
10
13
|
* @prop {T} [defaultValue]
|
|
11
14
|
*/
|
|
12
15
|
|
|
@@ -57,7 +60,7 @@ const styleElementCache = new Map();
|
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
62
|
* @param {TemplateStringsArray} array
|
|
60
|
-
* @param {...
|
|
63
|
+
* @param {...any} substitutions
|
|
61
64
|
* @return {HTMLStyleElement|CSSStyleSheet}
|
|
62
65
|
*/
|
|
63
66
|
export function css(array, ...substitutions) {
|
|
@@ -76,18 +79,20 @@ export function css(array, ...substitutions) {
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
if (_cssStyleSheetConstructable) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
sheet = new CSSStyleSheet();
|
|
82
|
-
_cssStyleSheetConstructable = true;
|
|
83
|
-
sheet.replaceSync(content);
|
|
84
|
-
cssStyleSheetsCache.set(content, sheet);
|
|
82
|
+
if (cssStyleSheetsCache.has(content)) {
|
|
83
|
+
return cssStyleSheetsCache.get(content);
|
|
85
84
|
}
|
|
85
|
+
const sheet = new CSSStyleSheet();
|
|
86
|
+
_cssStyleSheetConstructable = true;
|
|
87
|
+
sheet.replaceSync(content);
|
|
88
|
+
cssStyleSheetsCache.set(content, sheet);
|
|
86
89
|
return sheet;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
let style
|
|
90
|
-
if (
|
|
92
|
+
let style;
|
|
93
|
+
if (styleElementCache.has(content)) {
|
|
94
|
+
style = styleElementCache.get(content);
|
|
95
|
+
} else {
|
|
91
96
|
_inactiveDocument ??= document.implementation.createHTMLDocument();
|
|
92
97
|
style = _inactiveDocument.createElement('style');
|
|
93
98
|
style.textContent = content;
|
|
@@ -146,3 +151,4 @@ export function html(strings, ...substitutions) {
|
|
|
146
151
|
|
|
147
152
|
return /** @type {DocumentFragment} */ (fragment.cloneNode(true));
|
|
148
153
|
}
|
|
154
|
+
|