@shortfuse/materialdesignweb 0.5.0 → 0.7.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 +155 -77
- package/bin/generate-css.js +12 -0
- package/components/Badge.css +30 -0
- package/components/Badge.js +15 -0
- package/components/Body.css +14 -0
- package/components/Body.js +7 -0
- package/components/BottomAppBar.css +23 -0
- package/components/BottomAppBar.js +25 -0
- package/components/Box.css +31 -0
- package/components/Box.js +24 -0
- package/components/Button.css +146 -0
- package/components/Button.js +95 -0
- package/components/Button.md +61 -0
- package/components/Card.css +109 -0
- package/components/Card.js +82 -0
- package/components/Checkbox.css +77 -0
- package/components/Checkbox.js +59 -0
- package/components/CheckboxIcon.css +89 -0
- package/components/CheckboxIcon.js +41 -0
- package/components/Chip.css +35 -0
- package/components/Chip.js +22 -0
- package/components/Dialog.css +235 -0
- package/components/Dialog.js +327 -0
- package/components/DialogActions.js +13 -0
- package/components/Divider.css +41 -0
- package/components/Divider.js +13 -0
- package/components/ExtendedFab.css +24 -0
- package/components/ExtendedFab.js +11 -0
- package/components/Fab.css +23 -0
- package/components/Fab.js +26 -0
- package/components/FilterChip.css +80 -0
- package/components/FilterChip.js +51 -0
- package/components/Headline.css +14 -0
- package/components/Headline.js +33 -0
- package/components/Icon.css +76 -0
- package/components/Icon.js +174 -0
- package/components/IconButton.css +151 -0
- package/components/IconButton.js +65 -0
- package/components/Input.js +16 -0
- package/components/Label.css +14 -0
- package/components/Label.js +7 -0
- package/components/Layout.css +19 -0
- package/components/Layout.js +12 -0
- package/components/List.css +12 -0
- package/components/List.js +17 -0
- package/components/ListItem.css +224 -0
- package/components/ListItem.js +112 -0
- package/components/ListOption.css +34 -0
- package/components/ListOption.js +122 -0
- package/components/ListSelect.css +9 -0
- package/components/ListSelect.js +206 -0
- package/components/Menu.css +171 -0
- package/components/Menu.js +470 -0
- package/components/MenuItem.css +53 -0
- package/components/MenuItem.js +215 -0
- package/components/Nav.css +17 -0
- package/components/Nav.js +23 -0
- package/components/NavBar.css +34 -0
- package/components/NavBar.js +88 -0
- package/components/NavBarItem.css +41 -0
- package/components/NavBarItem.js +7 -0
- package/components/NavDrawer.css +31 -0
- package/components/NavDrawer.js +13 -0
- package/components/NavDrawerItem.css +42 -0
- package/components/NavDrawerItem.js +12 -0
- package/components/NavItem.css +181 -0
- package/components/NavItem.js +83 -0
- package/components/NavRail.css +47 -0
- package/components/NavRail.js +17 -0
- package/components/NavRailItem.css +25 -0
- package/components/NavRailItem.js +7 -0
- package/components/Option.js +91 -0
- package/components/Outline.css +138 -0
- package/components/Pane.css +261 -0
- package/components/Pane.js +21 -0
- package/components/Progress.css +74 -0
- package/components/Progress.js +67 -0
- package/components/ProgressCircle.css +226 -0
- package/components/ProgressLine.css +155 -0
- package/components/Radio.css +83 -0
- package/components/Radio.js +42 -0
- package/components/RadioIcon.css +73 -0
- package/components/RadioIcon.js +37 -0
- package/components/Ripple.css +74 -0
- package/components/Ripple.js +114 -0
- package/components/SegmentedButton.css +94 -0
- package/components/SegmentedButton.js +49 -0
- package/components/SegmentedButtonGroup.css +12 -0
- package/components/SegmentedButtonGroup.js +44 -0
- package/components/Select.css +52 -0
- package/components/Select.js +71 -0
- package/components/Shape.css +132 -0
- package/components/Shape.js +25 -0
- package/components/Slider.css +306 -0
- package/components/Slider.js +206 -0
- package/components/Snackbar.css +80 -0
- package/components/Snackbar.js +75 -0
- package/components/Surface.css +10 -0
- package/components/Surface.js +23 -0
- package/components/Switch.css +63 -0
- package/components/Switch.js +127 -0
- package/components/SwitchIcon.css +177 -0
- package/components/SwitchIcon.js +89 -0
- package/components/SwitchIconAnimations.css +89 -0
- package/components/Tab.css +85 -0
- package/components/Tab.js +103 -0
- package/components/TabContent.js +151 -0
- package/components/TabList.css +129 -0
- package/components/TabList.js +309 -0
- package/components/TabPanel.js +37 -0
- package/components/TextArea.css +93 -0
- package/components/TextArea.js +229 -0
- package/components/Title.css +14 -0
- package/components/Title.js +15 -0
- package/components/Tooltip.css +40 -0
- package/components/Tooltip.js +22 -0
- package/components/TopAppBar.css +209 -0
- package/components/TopAppBar.js +201 -0
- package/core/Composition.js +988 -0
- package/core/CustomElement.js +844 -0
- package/core/ICustomElement.d.ts +288 -0
- package/core/ICustomElement.js +1 -0
- package/core/css.js +51 -0
- package/core/customTypes.js +125 -0
- package/core/dom.js +56 -154
- package/core/identify.js +40 -0
- package/core/observe.js +410 -0
- package/core/template.js +121 -0
- package/core/typings.d.ts +135 -0
- package/core/typings.js +1 -0
- package/mixins/AriaReflectorMixin.js +42 -0
- package/mixins/AriaToolbarMixin.js +13 -0
- package/mixins/ControlMixin.css +57 -0
- package/mixins/ControlMixin.js +212 -0
- package/mixins/DensityMixin.css +40 -0
- package/mixins/DensityMixin.js +11 -0
- package/mixins/FlexableMixin.css +79 -0
- package/mixins/FlexableMixin.js +32 -0
- package/mixins/FormAssociatedMixin.js +170 -0
- package/mixins/InputMixin.js +335 -0
- package/mixins/KeyboardNavMixin.js +244 -0
- package/mixins/RTLObserverMixin.js +35 -0
- package/mixins/ResizeObserverMixin.js +38 -0
- package/mixins/RippleMixin.css +12 -0
- package/mixins/RippleMixin.js +115 -0
- package/mixins/ScrollListenerMixin.js +100 -0
- package/mixins/ShapeMixin.css +135 -0
- package/mixins/ShapeMixin.js +31 -0
- package/mixins/StateMixin.css +82 -0
- package/mixins/StateMixin.js +114 -0
- package/mixins/SurfaceMixin.css +150 -0
- package/mixins/SurfaceMixin.js +32 -0
- package/mixins/TextFieldMixin.css +657 -0
- package/mixins/TextFieldMixin.js +121 -0
- package/mixins/ThemableMixin.css +204 -0
- package/mixins/ThemableMixin.js +16 -0
- package/mixins/TooltipTriggerMixin.css +27 -0
- package/mixins/TooltipTriggerMixin.js +366 -0
- package/mixins/TouchTargetMixin.css +26 -0
- package/mixins/TouchTargetMixin.js +9 -0
- package/package.json +54 -49
- package/theming/index.js +594 -0
- package/theming/loader.js +24 -0
- package/utils/cli.js +11 -0
- package/utils/color_keywords.js +151 -0
- package/utils/hct/Cam16.js +298 -0
- package/utils/hct/CorePalette.js +84 -0
- package/utils/hct/Hct.js +172 -0
- package/utils/hct/Scheme.js +587 -0
- package/utils/hct/TonalPalette.js +68 -0
- package/utils/hct/ViewingConditions.js +136 -0
- package/utils/hct/blend.js +93 -0
- package/utils/hct/colorUtils.js +302 -0
- package/utils/hct/hctSolver.js +559 -0
- package/utils/hct/helper.js +182 -0
- package/utils/hct/mathUtils.js +153 -0
- package/utils/jsonMergePatch.js +100 -0
- package/utils/jsx-runtime.js +101 -0
- package/utils/popup.js +117 -0
- package/utils/svg.js +129 -0
- package/.browserslistrc +0 -4
- package/.eslintrc.json +0 -204
- package/.stylelintrc.json +0 -645
- package/.vscode/launch.json +0 -31
- package/.vscode/settings.json +0 -3
- package/.vscode/tasks.json +0 -32
- package/CHANGELOG.md +0 -36
- package/CODE_OF_CONDUCT.md +0 -46
- package/adapters/datatable/column.js +0 -176
- package/adapters/datatable/index.js +0 -960
- package/adapters/dom/index.js +0 -586
- package/adapters/list/index.js +0 -69
- package/adapters/search/index.js +0 -495
- package/components/appbar/_spec.scss +0 -165
- package/components/appbar/_theme.scss +0 -0
- package/components/appbar/index.scss +0 -2
- package/components/banner/_spec.scss +0 -83
- package/components/banner/_theme.scss +0 -0
- package/components/banner/index.scss +0 -2
- package/components/bottomnav/README.md +0 -85
- package/components/bottomnav/_spec.scss +0 -149
- package/components/bottomnav/_theme.scss +0 -0
- package/components/bottomnav/index.js +0 -117
- package/components/bottomnav/index.scss +0 -2
- package/components/bottomnav/item.js +0 -88
- package/components/button/README.md +0 -61
- package/components/button/_spec.scss +0 -162
- package/components/button/_theme.scss +0 -42
- package/components/button/index.eta +0 -32
- package/components/button/index.js +0 -43
- package/components/button/index.pug +0 -18
- package/components/button/index.scss +0 -2
- package/components/card/_spec.scss +0 -241
- package/components/card/_theme.scss +0 -0
- package/components/card/index.scss +0 -2
- package/components/chip/_spec.scss +0 -111
- package/components/chip/_theme.scss +0 -105
- package/components/chip/index.js +0 -23
- package/components/chip/index.scss +0 -2
- package/components/chip/item.js +0 -20
- package/components/datatable/_spec.scss +0 -225
- package/components/datatable/_theme.scss +0 -128
- package/components/datatable/cell.js +0 -44
- package/components/datatable/columnheader.js +0 -46
- package/components/datatable/index.js +0 -374
- package/components/datatable/index.scss +0 -2
- package/components/datatable/row.js +0 -48
- package/components/datatable/rowheader.js +0 -18
- package/components/dialog/_spec.scss +0 -203
- package/components/dialog/_theme.scss +0 -7
- package/components/dialog/index.js +0 -601
- package/components/dialog/index.scss +0 -2
- package/components/divider/_spec.scss +0 -11
- package/components/divider/_theme.scss +0 -0
- package/components/divider/index.scss +0 -2
- package/components/elevation/_spec.scss +0 -9
- package/components/elevation/_theme.scss +0 -0
- package/components/elevation/index.scss +0 -2
- package/components/fab/_spec.scss +0 -210
- package/components/fab/_theme.scss +0 -0
- package/components/fab/index.js +0 -99
- package/components/fab/index.scss +0 -2
- package/components/grid/_spec.scss +0 -169
- package/components/grid/_theme.scss +0 -0
- package/components/grid/index.scss +0 -2
- package/components/layout/_mixins.scss +0 -11
- package/components/layout/_spec.scss +0 -916
- package/components/layout/_theme.scss +0 -19
- package/components/layout/index.js +0 -454
- package/components/layout/index.scss +0 -2
- package/components/list/_spec.scss +0 -363
- package/components/list/_theme.scss +0 -102
- package/components/list/content.js +0 -106
- package/components/list/index.js +0 -256
- package/components/list/index.scss +0 -2
- package/components/list/item.js +0 -167
- package/components/list/secondary.js +0 -45
- package/components/menu/_spec.scss +0 -329
- package/components/menu/_theme.scss +0 -0
- package/components/menu/index.js +0 -705
- package/components/menu/index.scss +0 -2
- package/components/menu/item.js +0 -231
- package/components/progress/_spec.scss +0 -156
- package/components/progress/_theme.scss +0 -0
- package/components/progress/index.js +0 -36
- package/components/progress/index.scss +0 -2
- package/components/selection/_spec.scss +0 -376
- package/components/selection/_theme.scss +0 -134
- package/components/selection/index.eta +0 -60
- package/components/selection/index.js +0 -70
- package/components/selection/index.pug +0 -30
- package/components/selection/index.scss +0 -2
- package/components/selection/input.js +0 -54
- package/components/selection/radiogroup.js +0 -40
- package/components/slider/_spec.scss +0 -59
- package/components/slider/_theme.scss +0 -0
- package/components/slider/index.scss +0 -2
- package/components/snackbar/_spec.scss +0 -150
- package/components/snackbar/_theme.scss +0 -0
- package/components/snackbar/index.js +0 -338
- package/components/snackbar/index.scss +0 -2
- package/components/tab/_spec.scss +0 -220
- package/components/tab/_theme.scss +0 -0
- package/components/tab/content.js +0 -210
- package/components/tab/index.js +0 -257
- package/components/tab/index.scss +0 -2
- package/components/tab/item.js +0 -88
- package/components/tab/list.js +0 -196
- package/components/tab/panel.js +0 -54
- package/components/textfield/README.md +0 -179
- package/components/textfield/_spec.scss +0 -763
- package/components/textfield/_theme.scss +0 -264
- package/components/textfield/index.eta +0 -74
- package/components/textfield/index.js +0 -160
- package/components/textfield/index.pug +0 -30
- package/components/textfield/index.scss +0 -2
- package/components/tooltip/_spec.scss +0 -185
- package/components/tooltip/_theme.scss +0 -0
- package/components/tooltip/index.scss +0 -2
- package/components/type/_spec.scss +0 -227
- package/components/type/_theme.scss +0 -0
- package/components/type/index.scss +0 -2
- package/core/_breakpoint.scss +0 -189
- package/core/_elevation.scss +0 -78
- package/core/_length.scss +0 -8
- package/core/_motion.scss +0 -31
- package/core/_platform.scss +0 -12
- package/core/_type.scss +0 -128
- package/core/aria/attributes.js +0 -141
- package/core/aria/button.js +0 -49
- package/core/aria/keyboard.js +0 -92
- package/core/aria/rovingtabindex.js +0 -175
- package/core/aria/tab.js +0 -59
- package/core/document/index.js +0 -39
- package/core/overlay/_spec.scss +0 -28
- package/core/overlay/_theme.scss +0 -147
- package/core/overlay/index.js +0 -95
- package/core/overlay/index.scss +0 -2
- package/core/ripple/_spec.scss +0 -196
- package/core/ripple/_theme.scss +0 -20
- package/core/ripple/index.js +0 -286
- package/core/ripple/index.scss +0 -2
- package/core/theme/_aliases.scss +0 -15
- package/core/theme/_config.scss +0 -8
- package/core/theme/_functions.scss +0 -22
- package/core/theme/_palettes.scss +0 -405
- package/core/theme/_spec.scss +0 -0
- package/core/theme/_theme.scss +0 -268
- package/core/theme/index.js +0 -50
- package/core/theme/index.scss +0 -4
- package/core/throttler.js +0 -42
- package/core/transition/index.js +0 -465
- package/docs/_flex.scss +0 -28
- package/docs/_menuoptions.js +0 -183
- package/docs/_partials/_androidnavbar.eta +0 -5
- package/docs/_partials/_androidstatusbar.eta +0 -13
- package/docs/_partials/_appbar.eta +0 -27
- package/docs/_partials/_buttontest.eta +0 -31
- package/docs/_partials/_header.eta +0 -146
- package/docs/_partials/_navlistitem.eta +0 -16
- package/docs/_partials/_target.eta +0 -1
- package/docs/_sample-utils.js +0 -88
- package/docs/_storage.js +0 -33
- package/docs/docs.scss +0 -331
- package/docs/framework.scss +0 -26
- package/docs/index.eta +0 -12
- package/docs/index.js +0 -7
- package/docs/pages/appbar.eta +0 -108
- package/docs/pages/appbar.js +0 -0
- package/docs/pages/bottomnav.eta +0 -188
- package/docs/pages/bottomnav.js +0 -118
- package/docs/pages/button.eta +0 -124
- package/docs/pages/button.js +0 -224
- package/docs/pages/card.eta +0 -90
- package/docs/pages/card.js +0 -175
- package/docs/pages/chip.eta +0 -122
- package/docs/pages/chip.js +0 -80
- package/docs/pages/color.eta +0 -143
- package/docs/pages/color.js +0 -261
- package/docs/pages/datatable.eta +0 -323
- package/docs/pages/datatable.js +0 -160
- package/docs/pages/dialog.eta +0 -184
- package/docs/pages/dialog.js +0 -174
- package/docs/pages/dom.eta +0 -26
- package/docs/pages/dom.js +0 -140
- package/docs/pages/elevation.eta +0 -35
- package/docs/pages/elevation.js +0 -0
- package/docs/pages/fab.eta +0 -99
- package/docs/pages/fab.js +0 -43
- package/docs/pages/grid.eta +0 -135
- package/docs/pages/grid.js +0 -128
- package/docs/pages/layout.eta +0 -8
- package/docs/pages/layout.js +0 -0
- package/docs/pages/list.eta +0 -465
- package/docs/pages/list.js +0 -8
- package/docs/pages/menu.eta +0 -274
- package/docs/pages/menu.js +0 -213
- package/docs/pages/overlay.eta +0 -69
- package/docs/pages/overlay.js +0 -3
- package/docs/pages/progress.eta +0 -23
- package/docs/pages/progress.js +0 -12
- package/docs/pages/ripple.eta +0 -27
- package/docs/pages/ripple.js +0 -3
- package/docs/pages/search.eta +0 -242
- package/docs/pages/search.js +0 -226
- package/docs/pages/selection.eta +0 -107
- package/docs/pages/selection.js +0 -12
- package/docs/pages/slider.eta +0 -23
- package/docs/pages/slider.js +0 -0
- package/docs/pages/snackbar.eta +0 -83
- package/docs/pages/snackbar.js +0 -157
- package/docs/pages/tab.eta +0 -407
- package/docs/pages/tab.js +0 -152
- package/docs/pages/textfield.eta +0 -487
- package/docs/pages/textfield.js +0 -257
- package/docs/pages/tooltip.eta +0 -92
- package/docs/pages/tooltip.js +0 -0
- package/docs/pages/transition.eta +0 -117
- package/docs/pages/transition.js +0 -52
- package/docs/pages/type.eta +0 -31
- package/docs/pages/type.js +0 -0
- package/docs/postrender.js +0 -41
- package/docs/prerender.js +0 -16
- package/docs/pwa/_dialogs.eta +0 -143
- package/docs/pwa/_menus.eta +0 -16
- package/docs/pwa/pwa-prerender.js +0 -3
- package/docs/pwa/pwa.eta +0 -478
- package/docs/pwa/pwa.js +0 -298
- package/docs/pwa/pwa.scss +0 -31
- package/docs/themes/theme-colored.scss +0 -15
- package/docs/themes/theme-default.scss +0 -3
- package/index.scss +0 -27
- package/jsconfig.json +0 -16
- package/scripts/deploy-docs.sh +0 -9
- package/templates/index.eta +0 -2
- package/templates/index.pug +0 -3
- package/tsconfig.json +0 -16
- package/webpack.config.js +0 -304
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
|
|
3
|
+
import Composition from './Composition.js';
|
|
4
|
+
import { ICustomElement } from './ICustomElement.js';
|
|
5
|
+
import { attrNameFromPropName, attrValueFromDataValue } from './dom.js';
|
|
6
|
+
import { defineObservableProperty } from './observe.js';
|
|
7
|
+
import { addInlineFunction, css, html } from './template.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @template {abstract new (...args: any) => unknown} T
|
|
11
|
+
* @param {InstanceType<T>} instance
|
|
12
|
+
*/
|
|
13
|
+
function superOf(instance) {
|
|
14
|
+
const staticContext = instance.constructor;
|
|
15
|
+
const superOfStatic = Object.getPrototypeOf(staticContext);
|
|
16
|
+
return superOfStatic.prototype;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const EVENT_PREFIX_REGEX = /^([*1~]+)?(.*)$/;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Web Component that can cache templates for minification or performance
|
|
23
|
+
*/
|
|
24
|
+
export default class CustomElement extends ICustomElement {
|
|
25
|
+
/** @type {string} */
|
|
26
|
+
static elementName;
|
|
27
|
+
|
|
28
|
+
/** @return {Iterable<string>} */
|
|
29
|
+
static get observedAttributes() {
|
|
30
|
+
const s = new Set();
|
|
31
|
+
for (const config of this.propList.values()) {
|
|
32
|
+
if (config.reflect === true || config.reflect === 'read') {
|
|
33
|
+
s.add(config.attr);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return s;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @type {import('./Composition.js').Compositor<?>} */
|
|
40
|
+
compose() {
|
|
41
|
+
if (this.#composition) {
|
|
42
|
+
console.warn('Already composed. Generating *new* composition...');
|
|
43
|
+
}
|
|
44
|
+
this.#composition = new Composition();
|
|
45
|
+
return this.#composition;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @type {Composition<?>} */
|
|
49
|
+
static _composition = null;
|
|
50
|
+
|
|
51
|
+
/** @type {Map<string, import('./typings.js').ObserverConfiguration<?,?,?>>} */
|
|
52
|
+
static _props = new Map();
|
|
53
|
+
|
|
54
|
+
/** @type {Map<string, Function[]>} */
|
|
55
|
+
static _propChangedCallbacks = new Map();
|
|
56
|
+
|
|
57
|
+
/** @type {Map<string, Function[]>} */
|
|
58
|
+
static _attributeChangedCallbacks = new Map();
|
|
59
|
+
|
|
60
|
+
/** @type {typeof ICustomElement._onComposeCallbacks} */
|
|
61
|
+
static _onComposeCallbacks = [];
|
|
62
|
+
|
|
63
|
+
/** @type {typeof ICustomElement._onConnectedCallbacks} */
|
|
64
|
+
static _onConnectedCallbacks = [];
|
|
65
|
+
|
|
66
|
+
/** @type {typeof ICustomElement._onDisconnectedCallbacks} */
|
|
67
|
+
static _onDisconnectedCallbacks = [];
|
|
68
|
+
|
|
69
|
+
/** @type {typeof ICustomElement._onConstructedCallbacks} */
|
|
70
|
+
static _onConstructedCallbacks = [];
|
|
71
|
+
|
|
72
|
+
static interpolatesTemplate = true;
|
|
73
|
+
|
|
74
|
+
static supportsElementInternals = 'attachInternals' in HTMLElement.prototype;
|
|
75
|
+
|
|
76
|
+
static supportsElementInternalsRole = CustomElement.supportsElementInternals
|
|
77
|
+
&& 'role' in ElementInternals.prototype;
|
|
78
|
+
|
|
79
|
+
/** @type {boolean} */
|
|
80
|
+
static templatable = null;
|
|
81
|
+
|
|
82
|
+
static defined = false;
|
|
83
|
+
|
|
84
|
+
static autoRegistration = true;
|
|
85
|
+
|
|
86
|
+
/** @type {Map<string, typeof CustomElement>} */
|
|
87
|
+
static registrations = new Map();
|
|
88
|
+
|
|
89
|
+
/** @type {typeof ICustomElement.expressions} */
|
|
90
|
+
static expressions = this.set;
|
|
91
|
+
|
|
92
|
+
/** @type {typeof ICustomElement.methods} */
|
|
93
|
+
static methods = this.set;
|
|
94
|
+
|
|
95
|
+
/** @type {typeof ICustomElement.overrides} */
|
|
96
|
+
static overrides = this.set;
|
|
97
|
+
|
|
98
|
+
/** @type {typeof ICustomElement.props} */
|
|
99
|
+
static props = this.observe;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @template {typeof CustomElement} T
|
|
103
|
+
* @this T
|
|
104
|
+
* @template {keyof T} K
|
|
105
|
+
* @param {K} collection
|
|
106
|
+
* @param {T[K] extends (infer R)[] ? R : never} callback
|
|
107
|
+
*/
|
|
108
|
+
static _addCallback(collection, callback) {
|
|
109
|
+
if (!this.hasOwnProperty(collection)) {
|
|
110
|
+
this[collection] = [
|
|
111
|
+
...this[collection],
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
this[collection].push(callback);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Append parts to composition
|
|
119
|
+
* @type {typeof ICustomElement.append}
|
|
120
|
+
*/
|
|
121
|
+
static append(...parts) {
|
|
122
|
+
this.on({
|
|
123
|
+
composed({ composition }) {
|
|
124
|
+
// console.debug('onComposed:append', ...parts);
|
|
125
|
+
composition.append(...parts);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
// @ts-expect-error Can't cast T
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Appends styles to composition
|
|
134
|
+
* @type {typeof ICustomElement.css}
|
|
135
|
+
*/
|
|
136
|
+
static css(array, ...substitutions) {
|
|
137
|
+
if (Array.isArray(array)) {
|
|
138
|
+
// @ts-expect-error Complex cast
|
|
139
|
+
this.append(css(array, ...substitutions));
|
|
140
|
+
} else {
|
|
141
|
+
// @ts-expect-error Complex cast
|
|
142
|
+
this.append(array, ...substitutions);
|
|
143
|
+
}
|
|
144
|
+
// @ts-expect-error Can't cast T
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** @type {typeof ICustomElement['setSchema']} */
|
|
149
|
+
static setSchema(schema) {
|
|
150
|
+
this.schema = schema;
|
|
151
|
+
// @ts-expect-error Can't cast T
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Registers class asynchronously at end of current event loop cycle
|
|
157
|
+
* via `queueMicrotask`. If class is registered before then,
|
|
158
|
+
* does nothing.
|
|
159
|
+
* @type {typeof ICustomElement['autoRegister']}
|
|
160
|
+
*/
|
|
161
|
+
static autoRegister(elementName) {
|
|
162
|
+
if (elementName) {
|
|
163
|
+
this.elementName = elementName;
|
|
164
|
+
}
|
|
165
|
+
queueMicrotask(() => {
|
|
166
|
+
if (this.autoRegistration) {
|
|
167
|
+
this.register();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// @ts-expect-error Can't cast T
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Appends DocumentFragment to composition
|
|
176
|
+
* @type {typeof ICustomElement.html}
|
|
177
|
+
*/
|
|
178
|
+
static html(strings, ...substitutions) {
|
|
179
|
+
this.on({
|
|
180
|
+
composed({ composition }) {
|
|
181
|
+
// console.log('onComposed:html', strings);
|
|
182
|
+
composition.append(html(strings, ...substitutions));
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
// @ts-expect-error Can't cast T
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Extends base class into a new class.
|
|
191
|
+
* Use to avoid mutating base class.
|
|
192
|
+
* TODO: Add constructor arguments typing
|
|
193
|
+
* @type {typeof ICustomElement.extend}
|
|
194
|
+
*/
|
|
195
|
+
static extend() {
|
|
196
|
+
// @ts-expect-error Can't cast T
|
|
197
|
+
return class ExtendedClass extends this {};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Extends base class into a new class.
|
|
202
|
+
* Use to avoid mutating base class.
|
|
203
|
+
* TODO: Add constructor arguments typing
|
|
204
|
+
* @type {typeof ICustomElement.tsClassFix}
|
|
205
|
+
*/
|
|
206
|
+
static tsClassFix() {
|
|
207
|
+
// @ts-expect-error Can't cast T
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Assigns static values to class
|
|
213
|
+
* @type {typeof ICustomElement.setStatic}
|
|
214
|
+
*/
|
|
215
|
+
static setStatic(source) {
|
|
216
|
+
Object.assign(this, source);
|
|
217
|
+
// @ts-expect-error Can't cast T
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Assigns values directly to all instances (via prototype)
|
|
223
|
+
* @type {typeof ICustomElement.set}
|
|
224
|
+
*/
|
|
225
|
+
static readonly(source, options) {
|
|
226
|
+
// @ts-expect-error Can't cast T
|
|
227
|
+
return this.set(source, { ...options, writable: false });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Assigns values directly to all instances (via prototype)
|
|
232
|
+
* @type {typeof ICustomElement.set}
|
|
233
|
+
*/
|
|
234
|
+
static set(source, options) {
|
|
235
|
+
Object.defineProperties(
|
|
236
|
+
this.prototype,
|
|
237
|
+
Object.fromEntries(
|
|
238
|
+
Object.entries(source).map(([name, value]) => {
|
|
239
|
+
// Tap into .map() to avoid double iteration
|
|
240
|
+
// Property may be redefined observable
|
|
241
|
+
this.undefine(name);
|
|
242
|
+
return [
|
|
243
|
+
name,
|
|
244
|
+
{
|
|
245
|
+
enumerable: name[0] !== '_',
|
|
246
|
+
configurable: true,
|
|
247
|
+
value,
|
|
248
|
+
writable: true,
|
|
249
|
+
...options,
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}),
|
|
253
|
+
),
|
|
254
|
+
);
|
|
255
|
+
// @ts-expect-error Can't cast T
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Returns result of calling mixin with current class
|
|
261
|
+
* @type {typeof ICustomElement.mixin}
|
|
262
|
+
*/
|
|
263
|
+
static mixin(mixin) {
|
|
264
|
+
return mixin(this);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Registers class with window.customElements synchronously
|
|
269
|
+
* @type {typeof ICustomElement['register']}
|
|
270
|
+
*/
|
|
271
|
+
static register(elementName, force = false) {
|
|
272
|
+
if (this.hasOwnProperty('defined') && this.defined && !force) {
|
|
273
|
+
console.warn(this.elementName, 'already registered.');
|
|
274
|
+
// @ts-expect-error Can't cast T
|
|
275
|
+
return this;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (elementName) {
|
|
279
|
+
this.elementName = elementName;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
customElements.define(this.elementName, this);
|
|
283
|
+
CustomElement.registrations.set(this.elementName, this);
|
|
284
|
+
this.defined = true;
|
|
285
|
+
// @ts-expect-error Can't cast T
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static get propList() {
|
|
290
|
+
if (!this.hasOwnProperty('_props')) {
|
|
291
|
+
this._props = new Map(this._props);
|
|
292
|
+
}
|
|
293
|
+
return this._props;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static get propChangedCallbacks() {
|
|
297
|
+
if (!this.hasOwnProperty('_propChangedCallbacks')) {
|
|
298
|
+
// structuredClone()
|
|
299
|
+
this._propChangedCallbacks = new Map(
|
|
300
|
+
[
|
|
301
|
+
...this._propChangedCallbacks,
|
|
302
|
+
].map(([name, array]) => [name, array.slice()]),
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
return this._propChangedCallbacks;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static get attributeChangedCallbacks() {
|
|
309
|
+
if (!this.hasOwnProperty('_attributeChangedCallbacks')) {
|
|
310
|
+
this._attributeChangedCallbacks = new Map(
|
|
311
|
+
[
|
|
312
|
+
...this._attributeChangedCallbacks,
|
|
313
|
+
].map(([name, array]) => [name, array.slice()]),
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
return this._attributeChangedCallbacks;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Creates observable property on instances (via prototype)
|
|
321
|
+
* @template {import('./typings.js').ObserverPropertyType} [T1=null]
|
|
322
|
+
* @template {import('./typings.js').ObserverPropertyType} [T2=null]
|
|
323
|
+
* @template {any} [T3=null]
|
|
324
|
+
* @param {string} name
|
|
325
|
+
* @param {T1|import('./typings.js').ObserverOptions<T2,T3>} [typeOrOptions='string']
|
|
326
|
+
* @return {(
|
|
327
|
+
* T3 extends null ?
|
|
328
|
+
* T2 extends null ?
|
|
329
|
+
* T1 extends null ?
|
|
330
|
+
* string
|
|
331
|
+
* : import('./typings.js').ParsedObserverPropertyType<T1>
|
|
332
|
+
* : import('./typings.js').ParsedObserverPropertyType<T2>
|
|
333
|
+
* : T3
|
|
334
|
+
* )}
|
|
335
|
+
*/
|
|
336
|
+
static prop(name, typeOrOptions) {
|
|
337
|
+
// TODO: Cache and save configuration for reuse (mixins)
|
|
338
|
+
/** @type {import('./typings.js').ObserverOptions<?,?>} */
|
|
339
|
+
const options = {
|
|
340
|
+
...((typeof typeOrOptions === 'string') ? { type: typeOrOptions } : typeOrOptions),
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const customCallback = options.changedCallback;
|
|
344
|
+
|
|
345
|
+
if (customCallback) {
|
|
346
|
+
// Move callback to later in stack for attribute-based changes as well
|
|
347
|
+
this.onPropChanged({ [name]: customCallback });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// TODO: Inspect possible closure bloat
|
|
351
|
+
options.changedCallback = function wrappedChangedCallback(oldValue, newValue, changes) {
|
|
352
|
+
this._onObserverPropertyChanged.call(this, name, oldValue, newValue, changes);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const config = defineObservableProperty(this.prototype, name, options);
|
|
356
|
+
|
|
357
|
+
this.propList.set(name, config);
|
|
358
|
+
for (const [prop, callback] of config.watchers) {
|
|
359
|
+
this.on(`${prop}Changed`, callback);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return config.INIT_SYMBOL;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Define properties on instances via Object.defineProperties().
|
|
367
|
+
* Automatically sets property non-enumerable if name begins with `_`.
|
|
368
|
+
* @type {typeof ICustomElement.define}
|
|
369
|
+
*/
|
|
370
|
+
static define(props) {
|
|
371
|
+
Object.defineProperties(
|
|
372
|
+
this.prototype,
|
|
373
|
+
Object.fromEntries(
|
|
374
|
+
Object.entries(props).map(([name, options]) => {
|
|
375
|
+
// Tap into .map() to avoid double iteration
|
|
376
|
+
// Property may be redefined observable
|
|
377
|
+
this.undefine(name);
|
|
378
|
+
return [
|
|
379
|
+
name,
|
|
380
|
+
{
|
|
381
|
+
enumerable: name[0] !== '_',
|
|
382
|
+
configurable: true,
|
|
383
|
+
...(
|
|
384
|
+
typeof options === 'function'
|
|
385
|
+
? { get: options }
|
|
386
|
+
: options
|
|
387
|
+
),
|
|
388
|
+
},
|
|
389
|
+
];
|
|
390
|
+
}),
|
|
391
|
+
),
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// @ts-expect-error Can't cast T
|
|
395
|
+
return this;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
static undefine(name) {
|
|
399
|
+
Reflect.deleteProperty(this.prototype, name);
|
|
400
|
+
const config = this.propList.get(name);
|
|
401
|
+
if (config && config.watchers.length) {
|
|
402
|
+
const propWatchers = this.propChangedCallbacks.get(name);
|
|
403
|
+
if (propWatchers) {
|
|
404
|
+
for (const watcher of config.watchers) {
|
|
405
|
+
const index = propWatchers.indexOf(watcher);
|
|
406
|
+
if (index !== -1) {
|
|
407
|
+
console.warn('Unwatching', name);
|
|
408
|
+
propWatchers.splice(index, 1);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
this.propList.delete(name);
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Creates observable properties on instances
|
|
419
|
+
* @type {typeof ICustomElement.observe}
|
|
420
|
+
*/
|
|
421
|
+
static observe(props) {
|
|
422
|
+
for (const [name, typeOrOptions] of Object.entries(props ?? {})) {
|
|
423
|
+
if (typeof typeOrOptions === 'function') {
|
|
424
|
+
this.prop(name, {
|
|
425
|
+
reflect: false,
|
|
426
|
+
get: typeOrOptions,
|
|
427
|
+
});
|
|
428
|
+
} else {
|
|
429
|
+
this.prop(name, typeOrOptions);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// @ts-expect-error Can't cast T
|
|
433
|
+
return this;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** @type {typeof ICustomElement.defineStatic} */
|
|
437
|
+
static defineStatic(props) {
|
|
438
|
+
for (const [name, typeOrOptions] of Object.entries(props ?? {})) {
|
|
439
|
+
const options = (typeof typeOrOptions === 'function')
|
|
440
|
+
? { get: typeOrOptions }
|
|
441
|
+
: (typeof typeOrOptions === 'string'
|
|
442
|
+
? { type: typeOrOptions }
|
|
443
|
+
: typeOrOptions);
|
|
444
|
+
defineObservableProperty(this, name, {
|
|
445
|
+
reflect: false,
|
|
446
|
+
...options,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
// @ts-expect-error Can't cast T
|
|
450
|
+
return this;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** @type {typeof ICustomElement.events} */
|
|
454
|
+
static events(listeners, options) {
|
|
455
|
+
this.on({
|
|
456
|
+
composed({ composition }) {
|
|
457
|
+
for (const [key, listenerOptions] of Object.entries(listeners)) {
|
|
458
|
+
const [, flags, type] = key.match(EVENT_PREFIX_REGEX);
|
|
459
|
+
composition.addCompositionEventListener({
|
|
460
|
+
type,
|
|
461
|
+
once: flags?.includes('1'),
|
|
462
|
+
passive: flags?.includes('~'),
|
|
463
|
+
capture: flags?.includes('*'),
|
|
464
|
+
...(
|
|
465
|
+
typeof listenerOptions === 'function'
|
|
466
|
+
? { handleEvent: listenerOptions }
|
|
467
|
+
: (typeof listenerOptions === 'string'
|
|
468
|
+
? { prop: listenerOptions }
|
|
469
|
+
: listenerOptions)
|
|
470
|
+
),
|
|
471
|
+
...(
|
|
472
|
+
options
|
|
473
|
+
)
|
|
474
|
+
,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// @ts-expect-error Can't cast T
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** @type {typeof ICustomElement.childEvents} */
|
|
485
|
+
static childEvents(listenerMap, options) {
|
|
486
|
+
for (const [id, listeners] of Object.entries(listenerMap)) {
|
|
487
|
+
this.events(listeners, {
|
|
488
|
+
id,
|
|
489
|
+
...options,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// @ts-expect-error Can't cast T
|
|
494
|
+
return this;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** @type {typeof ICustomElement['on']} */
|
|
498
|
+
static on(nameOrCallbacks, callback) {
|
|
499
|
+
const callbacks = typeof nameOrCallbacks === 'string'
|
|
500
|
+
? { [nameOrCallbacks]: callback }
|
|
501
|
+
: nameOrCallbacks;
|
|
502
|
+
for (const [name, fn] of Object.entries(callbacks)) {
|
|
503
|
+
/** @type {keyof (typeof CustomElement)} */
|
|
504
|
+
let arrayPropName;
|
|
505
|
+
switch (name) {
|
|
506
|
+
case 'composed': arrayPropName = '_onComposeCallbacks'; break;
|
|
507
|
+
case 'constructed': arrayPropName = '_onConstructedCallbacks'; break;
|
|
508
|
+
case 'connected': arrayPropName = '_onConnectedCallbacks'; break;
|
|
509
|
+
case 'disconnected': arrayPropName = '_onDisconnectedCallbacks'; break;
|
|
510
|
+
case 'props':
|
|
511
|
+
this.onPropChanged(fn);
|
|
512
|
+
continue;
|
|
513
|
+
case 'attrs':
|
|
514
|
+
this.onAttributeChanged(fn);
|
|
515
|
+
continue;
|
|
516
|
+
default:
|
|
517
|
+
if (name.endsWith('Changed')) {
|
|
518
|
+
const prop = name.slice(0, name.length - 'Changed'.length);
|
|
519
|
+
this.onPropChanged({ [prop]: fn });
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
throw new Error('Invalid callback name');
|
|
523
|
+
}
|
|
524
|
+
this._addCallback(arrayPropName, fn);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// @ts-expect-error Can't cast T
|
|
528
|
+
return this;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** @type {typeof ICustomElement['onPropChanged']} */
|
|
532
|
+
static onPropChanged(options) {
|
|
533
|
+
for (const [prop, callback] of Object.entries(options)) {
|
|
534
|
+
let array = this.propChangedCallbacks.get(prop);
|
|
535
|
+
if (!array) {
|
|
536
|
+
array = [];
|
|
537
|
+
this.propChangedCallbacks.set(prop, array);
|
|
538
|
+
}
|
|
539
|
+
array.push(callback);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// @ts-expect-error Can't cast T
|
|
543
|
+
return this;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** @type {typeof ICustomElement['onAttributeChanged']} */
|
|
547
|
+
static onAttributeChanged(options) {
|
|
548
|
+
for (const [name, callback] of Object.entries(options)) {
|
|
549
|
+
let array = this.attributeChangedCallbacks.get(name);
|
|
550
|
+
if (!array) {
|
|
551
|
+
array = [];
|
|
552
|
+
this.attributeChangedCallbacks.set(name, array);
|
|
553
|
+
}
|
|
554
|
+
array.push(callback);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// @ts-expect-error Can't cast T
|
|
558
|
+
return this;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** @type {Record<string, HTMLElement>}} */
|
|
562
|
+
#refsProxy;
|
|
563
|
+
|
|
564
|
+
/** @type {Map<string, WeakRef<HTMLElement>>}} */
|
|
565
|
+
#refsCache = new Map();
|
|
566
|
+
|
|
567
|
+
/** @type {Map<string, WeakRef<HTMLElement>>}} */
|
|
568
|
+
#refsCompositionCache = new Map();
|
|
569
|
+
|
|
570
|
+
/** @type {Composition<?>} */
|
|
571
|
+
#composition;
|
|
572
|
+
|
|
573
|
+
/** @type {Map<string,null|[string,any]>} */
|
|
574
|
+
_propAttributeCache;
|
|
575
|
+
|
|
576
|
+
/** @type {import('./ICustomElement.js').CallbackArguments} */
|
|
577
|
+
_callbackArguments = null;
|
|
578
|
+
|
|
579
|
+
/** @param {any[]} args */
|
|
580
|
+
constructor(...args) {
|
|
581
|
+
super();
|
|
582
|
+
|
|
583
|
+
if (CustomElement.supportsElementInternals) {
|
|
584
|
+
this.elementInternals = this.attachInternals();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
this.attachShadow({ mode: 'open', delegatesFocus: this.delegatesFocus });
|
|
588
|
+
|
|
589
|
+
this.composition.initialRender(this.shadowRoot, this);
|
|
590
|
+
|
|
591
|
+
for (const callback of this.static._onConstructedCallbacks) {
|
|
592
|
+
callback.call(this, this.callbackArguments);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Updates nodes based on data
|
|
598
|
+
* Expects data in JSON Merge Patch format
|
|
599
|
+
* @see https://www.rfc-editor.org/rfc/rfc7386
|
|
600
|
+
* @param {?} data
|
|
601
|
+
* @param {?} [store]
|
|
602
|
+
* @return {void}
|
|
603
|
+
*/
|
|
604
|
+
render(data, store) {
|
|
605
|
+
// console.log('render', data);
|
|
606
|
+
this.composition.render(this.shadowRoot, data, this, store ? { ...this, store } : this);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/** @type {InstanceType<typeof ICustomElement>['propChangedCallback']} */
|
|
610
|
+
propChangedCallback(name, oldValue, newValue, changes = newValue) {
|
|
611
|
+
const callbacks = this.static.propChangedCallbacks.get(name);
|
|
612
|
+
if (callbacks) {
|
|
613
|
+
for (const callback of callbacks) {
|
|
614
|
+
callback.call(this, oldValue, newValue, changes, this);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
this.render({ [name]: changes });
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* @param {string} name
|
|
623
|
+
* @param {string|null} oldValue
|
|
624
|
+
* @param {string|null} newValue
|
|
625
|
+
*/
|
|
626
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
627
|
+
const callbacks = this.static.attributeChangedCallbacks.get(name);
|
|
628
|
+
if (callbacks) {
|
|
629
|
+
for (const callback of callbacks) {
|
|
630
|
+
callback.call(this, oldValue, newValue, this);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Array.find
|
|
635
|
+
for (const config of this.static.propList.values()) {
|
|
636
|
+
if (config.attr !== name) continue;
|
|
637
|
+
|
|
638
|
+
if (config.reflect !== true && config.reflect !== 'read') return;
|
|
639
|
+
|
|
640
|
+
if (config.attributeChangedCallback) {
|
|
641
|
+
config.attributeChangedCallback.call(this, name, oldValue, newValue);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const [stringValue] = this.attributeCache.get(name) ?? [null, null];
|
|
646
|
+
if (stringValue === newValue) {
|
|
647
|
+
// Attribute was changed via data change event. Ignore.
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// @ts-expect-error any
|
|
652
|
+
const previousDataValue = this[config.key];
|
|
653
|
+
const parsedValue = newValue === null
|
|
654
|
+
? config.nullParser(/** @type {null} */ (newValue))
|
|
655
|
+
// Avoid Boolean('') === false
|
|
656
|
+
: (config.type === 'boolean' ? true : config.parser(newValue));
|
|
657
|
+
|
|
658
|
+
if (parsedValue === previousDataValue) {
|
|
659
|
+
// No internal value change
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
// "Remember" that this attrValue equates to this data value
|
|
663
|
+
// Avoids rewriting attribute later on data change event
|
|
664
|
+
this.attributeCache.set(name, [newValue, parsedValue]);
|
|
665
|
+
// @ts-expect-error any
|
|
666
|
+
this[config.key] = parsedValue;
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
get #template() {
|
|
672
|
+
return this.#composition?.template;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* @param {string} name
|
|
677
|
+
* @param {any} oldValue
|
|
678
|
+
* @param {any} newValue
|
|
679
|
+
* @param {any} changes
|
|
680
|
+
*/
|
|
681
|
+
_onObserverPropertyChanged(name, oldValue, newValue, changes) {
|
|
682
|
+
const { reflect, attr } = this.static.propList.get(name);
|
|
683
|
+
if (attr && (reflect === true || reflect === 'write')) {
|
|
684
|
+
const [, dataValue] = this.attributeCache.get(attr) ?? [null, null];
|
|
685
|
+
// Don't change attribute if data value is equivalent
|
|
686
|
+
// (eg: Boolean('foo') === true; Number("1.0") === 1)
|
|
687
|
+
if (dataValue !== newValue) {
|
|
688
|
+
const attrValue = attrValueFromDataValue(newValue);
|
|
689
|
+
// Cache attrValue to ignore attributeChangedCallback later
|
|
690
|
+
this.attributeCache.set(attr, [attrValue, newValue]);
|
|
691
|
+
if (attrValue == null) {
|
|
692
|
+
this.removeAttribute(attr);
|
|
693
|
+
} else {
|
|
694
|
+
this.setAttribute(attr, attrValue);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Invoke change => render
|
|
700
|
+
this.propChangedCallback(name, oldValue, newValue, changes);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Proxy object that returns shadow DOM elements by ID.
|
|
705
|
+
* If called before interpolation (eg: on composed), returns from template
|
|
706
|
+
* @return {Record<string,HTMLElement>}
|
|
707
|
+
*/
|
|
708
|
+
get refs() {
|
|
709
|
+
// eslint-disable-next-line no-return-assign
|
|
710
|
+
return (this.#refsProxy ??= new Proxy({}, {
|
|
711
|
+
/**
|
|
712
|
+
* @param {any} target
|
|
713
|
+
* @param {string} id
|
|
714
|
+
* @return {Element}
|
|
715
|
+
*/
|
|
716
|
+
get: (target, id) => {
|
|
717
|
+
if (!this.#composition) {
|
|
718
|
+
console.warn(this.static.name, 'Attempted to access references before composing!');
|
|
719
|
+
}
|
|
720
|
+
const composition = this.composition;
|
|
721
|
+
if (!composition.interpolated) {
|
|
722
|
+
let element = this.#refsCompositionCache.get(id)?.deref();
|
|
723
|
+
if (element) return element;
|
|
724
|
+
const formattedId = attrNameFromPropName(id);
|
|
725
|
+
// console.warn(this.tagName, 'Returning template reference');
|
|
726
|
+
element = composition.template.getElementById(formattedId);
|
|
727
|
+
if (!element) return null;
|
|
728
|
+
this.#refsCompositionCache.set(id, new WeakRef(element));
|
|
729
|
+
return element;
|
|
730
|
+
}
|
|
731
|
+
let element = this.#refsCache.get(id)?.deref();
|
|
732
|
+
if (element) {
|
|
733
|
+
return element;
|
|
734
|
+
}
|
|
735
|
+
const formattedId = attrNameFromPropName(id);
|
|
736
|
+
element = composition.getElement(this.shadowRoot, formattedId);
|
|
737
|
+
if (!element) return null;
|
|
738
|
+
this.#refsCache.set(id, new WeakRef(element));
|
|
739
|
+
return element;
|
|
740
|
+
},
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
get attributeCache() {
|
|
745
|
+
this._propAttributeCache ??= new Map();
|
|
746
|
+
return this._propAttributeCache;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
get tabIndex() {
|
|
750
|
+
return super.tabIndex;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
set tabIndex(value) {
|
|
754
|
+
if (value === super.tabIndex && value !== -1) {
|
|
755
|
+
// Non -1 value already set
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (this.delegatesFocus && document.activeElement === this) {
|
|
760
|
+
if (this.getAttribute('tabindex') === value.toString()) {
|
|
761
|
+
// Skip if possible
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Chrome blurs on tabindex changes with delegatesFocus
|
|
766
|
+
// Fixed in Chrome 111
|
|
767
|
+
// Remove this code ~June 2023
|
|
768
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1346606
|
|
769
|
+
/** @type {EventListener} */
|
|
770
|
+
const listener = (e) => {
|
|
771
|
+
e.stopImmediatePropagation();
|
|
772
|
+
e.stopPropagation();
|
|
773
|
+
if (e.type === 'blur') {
|
|
774
|
+
console.warn('Chromium bug 1346606: Tabindex change caused blur. Giving focusing back.', this);
|
|
775
|
+
this.focus();
|
|
776
|
+
} else {
|
|
777
|
+
console.warn('Chromium bug 1346606: Blocking focus event.', this);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
this.addEventListener('blur', listener, { capture: true, once: true });
|
|
781
|
+
this.addEventListener('focus', listener, { capture: true, once: true });
|
|
782
|
+
super.tabIndex = value;
|
|
783
|
+
this.removeEventListener('blur', listener, { capture: true });
|
|
784
|
+
this.removeEventListener('focus', listener, { capture: true });
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
super.tabIndex = value;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
get static() { return /** @type {typeof CustomElement} */ (/** @type {unknown} */ (this.constructor)); }
|
|
792
|
+
|
|
793
|
+
get unique() { return false; }
|
|
794
|
+
|
|
795
|
+
get callbackArguments() {
|
|
796
|
+
// eslint-disable-next-line no-return-assign
|
|
797
|
+
return this._callbackArguments ??= {
|
|
798
|
+
composition: this.#composition,
|
|
799
|
+
html: html.bind(this),
|
|
800
|
+
inline: addInlineFunction,
|
|
801
|
+
template: this.#template,
|
|
802
|
+
element: this,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/** @return {Composition<?>} */
|
|
807
|
+
get composition() {
|
|
808
|
+
if (this.#composition) return this.#composition;
|
|
809
|
+
|
|
810
|
+
if (!this.unique && this.static.hasOwnProperty('_composition')) {
|
|
811
|
+
this.#composition = this.static._composition;
|
|
812
|
+
return this.static._composition;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// TODO: Use Composition to track uniqueness
|
|
816
|
+
// console.log('composing', this.static.elementName);
|
|
817
|
+
this.compose();
|
|
818
|
+
for (const callback of this.static._onComposeCallbacks) {
|
|
819
|
+
// console.log(this.static.elementName, 'composition callback');
|
|
820
|
+
callback.call(this, this.callbackArguments);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (!this.unique) {
|
|
824
|
+
// Cache compilation into static property
|
|
825
|
+
this.static._composition = this.#composition;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return this.#composition;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
connectedCallback() {
|
|
832
|
+
for (const callbacks of this.static._onConnectedCallbacks) {
|
|
833
|
+
callbacks.call(this, this.callbackArguments);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
disconnectedCallback() {
|
|
838
|
+
for (const callbacks of this.static._onDisconnectedCallbacks) {
|
|
839
|
+
callbacks.call(this, this.callbackArguments);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
CustomElement.prototype.delegatesFocus = false;
|