@universityofmaryland/web-model-library 1.0.7 → 1.1.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/LICENSE +5 -0
- package/README.md +1 -1
- package/dist/_types.d.ts +43 -0
- package/dist/_types.d.ts.map +1 -1
- package/dist/_virtual/index.js +1 -1
- package/dist/_virtual/postcss.js +1 -1
- package/dist/attributes/change-detection.d.ts +21 -0
- package/dist/attributes/change-detection.d.ts.map +1 -0
- package/dist/attributes/change-detection.js +60 -0
- package/dist/attributes/change-detection.js.map +1 -0
- package/dist/attributes/checks.d.ts.map +1 -1
- package/dist/attributes/checks.js +15 -8
- package/dist/attributes/checks.js.map +1 -1
- package/dist/attributes/config.d.ts +29 -0
- package/dist/attributes/config.d.ts.map +1 -0
- package/dist/attributes/config.js +57 -0
- package/dist/attributes/config.js.map +1 -0
- package/dist/attributes/converters.d.ts +14 -0
- package/dist/attributes/converters.d.ts.map +1 -0
- package/dist/attributes/converters.js +84 -0
- package/dist/attributes/converters.js.map +1 -0
- package/dist/attributes/errors.d.ts +16 -0
- package/dist/attributes/errors.d.ts.map +1 -0
- package/dist/attributes/errors.js +51 -0
- package/dist/attributes/errors.js.map +1 -0
- package/dist/attributes/index.d.ts +4 -0
- package/dist/attributes/index.d.ts.map +1 -1
- package/dist/attributes.js +18 -0
- package/dist/attributes.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/model/controllers.d.ts +22 -0
- package/dist/model/controllers.d.ts.map +1 -0
- package/dist/model/controllers.js +53 -0
- package/dist/model/controllers.js.map +1 -0
- package/dist/model/errors.d.ts +13 -0
- package/dist/model/errors.d.ts.map +1 -0
- package/dist/model/errors.js +23 -0
- package/dist/model/errors.js.map +1 -0
- package/dist/model/index.d.ts +54 -3
- package/dist/model/index.d.ts.map +1 -1
- package/dist/model/registration.d.ts +13 -0
- package/dist/model/registration.d.ts.map +1 -0
- package/dist/model/registration.js +83 -0
- package/dist/model/registration.js.map +1 -0
- package/dist/model/update-cycle.d.ts +12 -0
- package/dist/model/update-cycle.d.ts.map +1 -0
- package/dist/model/update-cycle.js +41 -0
- package/dist/model/update-cycle.js.map +1 -0
- package/dist/model.js +335 -55
- package/dist/model.js.map +1 -1
- package/dist/node_modules/.pnpm/nanoid@3.3.11/node_modules/nanoid/non-secure/index.js.map +1 -0
- package/dist/node_modules/{picocolors → .pnpm/picocolors@1.1.1/node_modules/picocolors}/picocolors.browser.js +1 -1
- package/dist/node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.browser.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss-discard-duplicates@5.1.0_postcss@8.5.8/node_modules/postcss-discard-duplicates/src/index.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/at-rule.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/comment.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/container.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/css-syntax-error.js +2 -2
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/css-syntax-error.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/declaration.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/document.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/fromJSON.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/input.js +2 -2
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/input.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/lazy-result.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/list.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/map-generator.js +1 -1
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/map-generator.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/no-work-result.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/node.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/parse.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/parser.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/postcss.js +1 -1
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss2.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/previous-map.js +1 -1
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/previous-map.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/processor.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/result.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/root.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/rule.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/stringifier.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/stringify.js.map +1 -0
- package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/symbols.js +1 -1
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/symbols.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/tokenize.js.map +1 -0
- package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/warning.js.map +1 -0
- package/dist/slots/index.d.ts +6 -0
- package/dist/slots/index.d.ts.map +1 -1
- package/dist/slots/slot-events.d.ts +32 -0
- package/dist/slots/slot-events.d.ts.map +1 -0
- package/dist/slots/slot-events.js +83 -0
- package/dist/slots/slot-events.js.map +1 -0
- package/dist/slots/slot-query.d.ts +18 -0
- package/dist/slots/slot-query.d.ts.map +1 -0
- package/dist/slots/slot-query.js +59 -0
- package/dist/slots/slot-query.js.map +1 -0
- package/dist/slots/slot-validation.d.ts +5 -0
- package/dist/slots/slot-validation.d.ts.map +1 -0
- package/dist/slots/slot-validation.js +89 -0
- package/dist/slots/slot-validation.js.map +1 -0
- package/dist/slots.js +15 -1
- package/dist/slots.js.map +1 -1
- package/dist/testing/console.d.ts +8 -0
- package/dist/testing/console.d.ts.map +1 -0
- package/dist/testing/console.js +39 -0
- package/dist/testing/console.js.map +1 -0
- package/dist/testing/events.d.ts +11 -0
- package/dist/testing/events.d.ts.map +1 -0
- package/dist/testing/events.js +47 -0
- package/dist/testing/events.js.map +1 -0
- package/dist/testing/fixture.d.ts +10 -0
- package/dist/testing/fixture.d.ts.map +1 -0
- package/dist/testing/fixture.js +36 -0
- package/dist/testing/fixture.js.map +1 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/shadow.d.ts +6 -0
- package/dist/testing/shadow.d.ts.map +1 -0
- package/dist/testing/shadow.js +27 -0
- package/dist/testing/shadow.js.map +1 -0
- package/dist/testing/slots.d.ts +10 -0
- package/dist/testing/slots.d.ts.map +1 -0
- package/dist/testing/slots.js +62 -0
- package/dist/testing/slots.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +26 -0
- package/dist/testing.js.map +1 -0
- package/dist/utilities/debug.d.ts +365 -0
- package/dist/utilities/debug.d.ts.map +1 -0
- package/dist/utilities/debug.js +71 -0
- package/dist/utilities/debug.js.map +1 -0
- package/dist/utilities/dom.d.ts +11 -0
- package/dist/utilities/dom.d.ts.map +1 -0
- package/dist/utilities/dom.js +57 -0
- package/dist/utilities/dom.js.map +1 -0
- package/dist/utilities/events.d.ts +22 -0
- package/dist/utilities/events.d.ts.map +1 -0
- package/dist/utilities/events.js +49 -0
- package/dist/utilities/events.js.map +1 -0
- package/dist/utilities/index.d.ts +7 -0
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/register.d.ts.map +1 -1
- package/dist/utilities/register.js +16 -4
- package/dist/utilities/register.js.map +1 -1
- package/dist/utilities/styles.js +1 -1
- package/dist/utilities/types.d.ts +10 -0
- package/dist/utilities/types.d.ts.map +1 -0
- package/dist/utilities/types.js +45 -0
- package/dist/utilities/types.js.map +1 -0
- package/dist/utilities.js +27 -1
- package/dist/utilities.js.map +1 -1
- package/package.json +21 -15
- package/dist/node_modules/nanoid/non-secure/index.js.map +0 -1
- package/dist/node_modules/picocolors/picocolors.browser.js.map +0 -1
- package/dist/node_modules/postcss/lib/at-rule.js.map +0 -1
- package/dist/node_modules/postcss/lib/comment.js.map +0 -1
- package/dist/node_modules/postcss/lib/container.js.map +0 -1
- package/dist/node_modules/postcss/lib/css-syntax-error.js.map +0 -1
- package/dist/node_modules/postcss/lib/declaration.js.map +0 -1
- package/dist/node_modules/postcss/lib/document.js.map +0 -1
- package/dist/node_modules/postcss/lib/fromJSON.js.map +0 -1
- package/dist/node_modules/postcss/lib/input.js.map +0 -1
- package/dist/node_modules/postcss/lib/lazy-result.js.map +0 -1
- package/dist/node_modules/postcss/lib/list.js.map +0 -1
- package/dist/node_modules/postcss/lib/map-generator.js.map +0 -1
- package/dist/node_modules/postcss/lib/no-work-result.js.map +0 -1
- package/dist/node_modules/postcss/lib/node.js.map +0 -1
- package/dist/node_modules/postcss/lib/parse.js.map +0 -1
- package/dist/node_modules/postcss/lib/parser.js.map +0 -1
- package/dist/node_modules/postcss/lib/postcss.js.map +0 -1
- package/dist/node_modules/postcss/lib/postcss2.js.map +0 -1
- package/dist/node_modules/postcss/lib/previous-map.js.map +0 -1
- package/dist/node_modules/postcss/lib/processor.js.map +0 -1
- package/dist/node_modules/postcss/lib/result.js.map +0 -1
- package/dist/node_modules/postcss/lib/root.js.map +0 -1
- package/dist/node_modules/postcss/lib/rule.js.map +0 -1
- package/dist/node_modules/postcss/lib/stringifier.js.map +0 -1
- package/dist/node_modules/postcss/lib/stringify.js.map +0 -1
- package/dist/node_modules/postcss/lib/symbols.js.map +0 -1
- package/dist/node_modules/postcss/lib/tokenize.js.map +0 -1
- package/dist/node_modules/postcss/lib/warning.js.map +0 -1
- package/dist/packages/model/node_modules/postcss-discard-duplicates/src/index.js.map +0 -1
- /package/dist/node_modules/{nanoid → .pnpm/nanoid@3.3.11/node_modules/nanoid}/non-secure/index.js +0 -0
- /package/dist/{packages/model → node_modules/.pnpm/postcss-discard-duplicates@5.1.0_postcss@8.5.8}/node_modules/postcss-discard-duplicates/src/index.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/at-rule.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/comment.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/container.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/declaration.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/document.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/fromJSON.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/lazy-result.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/list.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/no-work-result.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/node.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/parse.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/parser.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/postcss2.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/processor.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/result.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/root.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/rule.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/stringifier.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/stringify.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/tokenize.js +0 -0
- /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/warning.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isDev, createLogger } from "../utilities/debug.js";
|
|
2
|
+
import { ComponentRegistrationError, BatchRegistrationError } from "./errors.js";
|
|
3
|
+
const TAG_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
|
|
4
|
+
function registerComponent(tagName, element, options) {
|
|
5
|
+
const registry = options?.registry ?? window.customElements;
|
|
6
|
+
const eager = options?.eager ?? true;
|
|
7
|
+
if (!element || typeof element !== "function") {
|
|
8
|
+
throw new ComponentRegistrationError(
|
|
9
|
+
tagName,
|
|
10
|
+
"missing-constructor",
|
|
11
|
+
"Constructor is required and must be a function."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
if (!TAG_NAME_RE.test(tagName)) {
|
|
15
|
+
throw new ComponentRegistrationError(
|
|
16
|
+
tagName,
|
|
17
|
+
"invalid-name",
|
|
18
|
+
`"${tagName}" is not a valid custom element name. Must match ${TAG_NAME_RE}.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const existing = registry.get(tagName);
|
|
22
|
+
if (existing) {
|
|
23
|
+
if (existing === element) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
throw new ComponentRegistrationError(
|
|
27
|
+
tagName,
|
|
28
|
+
"conflict",
|
|
29
|
+
`Already registered with a different constructor.`,
|
|
30
|
+
existing
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (!eager) {
|
|
34
|
+
const hasElement = document.getElementsByTagName(tagName).length > 0;
|
|
35
|
+
if (!hasElement) {
|
|
36
|
+
if (isDev()) {
|
|
37
|
+
const logger = createLogger(tagName);
|
|
38
|
+
logger.debug("Lazy registration skipped — no DOM elements found.");
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
registry.define(tagName, element);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
function isComponentRegistered(tagName, registry) {
|
|
47
|
+
return (registry ?? window.customElements).get(tagName) !== void 0;
|
|
48
|
+
}
|
|
49
|
+
function whenComponentDefined(tagName, registry) {
|
|
50
|
+
return (registry ?? window.customElements).whenDefined(tagName);
|
|
51
|
+
}
|
|
52
|
+
function getComponentConstructor(tagName, registry) {
|
|
53
|
+
return (registry ?? window.customElements).get(tagName);
|
|
54
|
+
}
|
|
55
|
+
function registerComponents(components, options) {
|
|
56
|
+
const errors = [];
|
|
57
|
+
const succeeded = [];
|
|
58
|
+
for (const { tagName, element } of components) {
|
|
59
|
+
try {
|
|
60
|
+
const result = registerComponent(tagName, element, options);
|
|
61
|
+
if (result) {
|
|
62
|
+
succeeded.push(tagName);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error instanceof ComponentRegistrationError) {
|
|
66
|
+
errors.push(error);
|
|
67
|
+
} else {
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (errors.length > 0) {
|
|
73
|
+
throw new BatchRegistrationError(errors, succeeded);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
getComponentConstructor,
|
|
78
|
+
isComponentRegistered,
|
|
79
|
+
registerComponent,
|
|
80
|
+
registerComponents,
|
|
81
|
+
whenComponentDefined
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sources":["../../source/model/registration.ts"],"sourcesContent":["/**\n * Component registration utilities.\n *\n * Provides validated registration, idempotent same-constructor handling,\n * conflict detection, batch registration, and query helpers.\n */\n\nimport { isDev, createLogger } from '../utilities/debug';\nimport {\n ComponentRegistrationError,\n BatchRegistrationError,\n} from './errors';\n\nconst TAG_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;\n\nexport interface RegisterOptions {\n /** Register immediately (true, default) or only when DOM elements exist (false). */\n eager?: boolean;\n /** Custom element registry. Defaults to `window.customElements`. */\n registry?: CustomElementRegistry;\n}\n\n/**\n * Validate and register a custom element.\n *\n * - Returns `true` on successful registration.\n * - Returns `false` if the same constructor is already registered (idempotent).\n * - Throws `ComponentRegistrationError` on conflict, invalid name, or missing constructor.\n */\nexport function registerComponent(\n tagName: string,\n element: CustomElementConstructor,\n options?: RegisterOptions,\n): boolean {\n const registry = options?.registry ?? window.customElements;\n const eager = options?.eager ?? true;\n\n if (!element || typeof element !== 'function') {\n throw new ComponentRegistrationError(\n tagName,\n 'missing-constructor',\n 'Constructor is required and must be a function.',\n );\n }\n\n if (!TAG_NAME_RE.test(tagName)) {\n throw new ComponentRegistrationError(\n tagName,\n 'invalid-name',\n `\"${tagName}\" is not a valid custom element name. Must match ${TAG_NAME_RE}.`,\n );\n }\n\n const existing = registry.get(tagName);\n if (existing) {\n if (existing === element) {\n return false;\n }\n throw new ComponentRegistrationError(\n tagName,\n 'conflict',\n `Already registered with a different constructor.`,\n existing,\n );\n }\n\n if (!eager) {\n const hasElement = document.getElementsByTagName(tagName).length > 0;\n if (!hasElement) {\n if (isDev()) {\n const logger = createLogger(tagName);\n logger.debug('Lazy registration skipped — no DOM elements found.');\n }\n return false;\n }\n }\n\n registry.define(tagName, element);\n return true;\n}\n\n/**\n * Check whether a tag name is already registered.\n */\nexport function isComponentRegistered(\n tagName: string,\n registry?: CustomElementRegistry,\n): boolean {\n return (registry ?? window.customElements).get(tagName) !== undefined;\n}\n\n/**\n * Promise that resolves when a tag name is defined.\n */\nexport function whenComponentDefined(\n tagName: string,\n registry?: CustomElementRegistry,\n): Promise<CustomElementConstructor> {\n return (registry ?? window.customElements).whenDefined(tagName);\n}\n\n/**\n * Return the constructor for a registered tag, or `undefined`.\n */\nexport function getComponentConstructor(\n tagName: string,\n registry?: CustomElementRegistry,\n): CustomElementConstructor | undefined {\n return (registry ?? window.customElements).get(tagName);\n}\n\n/**\n * Register multiple components. Attempts all registrations; throws\n * `BatchRegistrationError` if any fail (with per-component errors attached).\n */\nexport function registerComponents(\n components: Array<{ tagName: string; element: CustomElementConstructor }>,\n options?: RegisterOptions,\n): void {\n const errors: ComponentRegistrationError[] = [];\n const succeeded: string[] = [];\n\n for (const { tagName, element } of components) {\n try {\n const result = registerComponent(tagName, element, options);\n if (result) {\n succeeded.push(tagName);\n }\n } catch (error) {\n if (error instanceof ComponentRegistrationError) {\n errors.push(error);\n } else {\n throw error;\n }\n }\n }\n\n if (errors.length > 0) {\n throw new BatchRegistrationError(errors, succeeded);\n }\n}\n"],"names":[],"mappings":";;AAaA,MAAM,cAAc;AAgBb,SAAS,kBACd,SACA,SACA,SACS;AACT,QAAM,WAAW,SAAS,YAAY,OAAO;AAC7C,QAAM,QAAQ,SAAS,SAAS;AAEhC,MAAI,CAAC,WAAW,OAAO,YAAY,YAAY;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,CAAC,YAAY,KAAK,OAAO,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,IAAI,OAAO,oDAAoD,WAAW;AAAA,IAAA;AAAA,EAE9E;AAEA,QAAM,WAAW,SAAS,IAAI,OAAO;AACrC,MAAI,UAAU;AACZ,QAAI,aAAa,SAAS;AACxB,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,CAAC,OAAO;AACV,UAAM,aAAa,SAAS,qBAAqB,OAAO,EAAE,SAAS;AACnE,QAAI,CAAC,YAAY;AACf,UAAI,SAAS;AACX,cAAM,SAAS,aAAa,OAAO;AACnC,eAAO,MAAM,oDAAoD;AAAA,MACnE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,OAAO,SAAS,OAAO;AAChC,SAAO;AACT;AAKO,SAAS,sBACd,SACA,UACS;AACT,UAAQ,YAAY,OAAO,gBAAgB,IAAI,OAAO,MAAM;AAC9D;AAKO,SAAS,qBACd,SACA,UACmC;AACnC,UAAQ,YAAY,OAAO,gBAAgB,YAAY,OAAO;AAChE;AAKO,SAAS,wBACd,SACA,UACsC;AACtC,UAAQ,YAAY,OAAO,gBAAgB,IAAI,OAAO;AACxD;AAMO,SAAS,mBACd,YACA,SACM;AACN,QAAM,SAAuC,CAAA;AAC7C,QAAM,YAAsB,CAAA;AAE5B,aAAW,EAAE,SAAS,QAAA,KAAa,YAAY;AAC7C,QAAI;AACF,YAAM,SAAS,kBAAkB,SAAS,SAAS,OAAO;AAC1D,UAAI,QAAQ;AACV,kBAAU,KAAK,OAAO;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,4BAA4B;AAC/C,eAAO,KAAK,KAAK;AAAA,MACnB,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,uBAAuB,QAAQ,SAAS;AAAA,EACpD;AACF;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class UpdateScheduler {
|
|
2
|
+
private _pending;
|
|
3
|
+
private _resolve;
|
|
4
|
+
private _promise;
|
|
5
|
+
private _performUpdate;
|
|
6
|
+
private _recursionCount;
|
|
7
|
+
constructor(performUpdate: () => boolean);
|
|
8
|
+
schedule(): void;
|
|
9
|
+
private _flush;
|
|
10
|
+
get updateComplete(): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=update-cycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-cycle.d.ts","sourceRoot":"","sources":["../../source/model/update-cycle.ts"],"names":[],"mappings":"AAaA,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAK;gBAEhB,aAAa,EAAE,MAAM,OAAO;IAKxC,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,MAAM;IAsBd,IAAI,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAErC;CACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const MAX_RECURSION = 100;
|
|
2
|
+
class UpdateScheduler {
|
|
3
|
+
constructor(performUpdate) {
|
|
4
|
+
this._pending = false;
|
|
5
|
+
this._resolve = null;
|
|
6
|
+
this._recursionCount = 0;
|
|
7
|
+
this._performUpdate = performUpdate;
|
|
8
|
+
this._promise = Promise.resolve(true);
|
|
9
|
+
}
|
|
10
|
+
schedule() {
|
|
11
|
+
if (this._pending) return;
|
|
12
|
+
this._pending = true;
|
|
13
|
+
this._promise = new Promise((resolve) => {
|
|
14
|
+
this._resolve = resolve;
|
|
15
|
+
queueMicrotask(() => this._flush(resolve));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
_flush(resolve) {
|
|
19
|
+
this._pending = false;
|
|
20
|
+
this._recursionCount++;
|
|
21
|
+
if (this._recursionCount > MAX_RECURSION) {
|
|
22
|
+
this._recursionCount = 0;
|
|
23
|
+
resolve(false);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const success = this._performUpdate();
|
|
27
|
+
if (this._pending) {
|
|
28
|
+
this._promise.then(resolve);
|
|
29
|
+
} else {
|
|
30
|
+
this._recursionCount = 0;
|
|
31
|
+
resolve(success);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get updateComplete() {
|
|
35
|
+
return this._promise;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
UpdateScheduler
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=update-cycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-cycle.js","sources":["../../source/model/update-cycle.ts"],"sourcesContent":["/**\n * Update Scheduler\n *\n * Batches multiple `schedule()` calls into a single microtask-based update.\n * Inspired by Lit's reactive update cycle.\n *\n * - Deduplicates: multiple `schedule()` calls before the microtask produce one update\n * - Re-entrant safe: if `performUpdate` triggers another `schedule()`, a new microtask is queued\n * - Max recursion guard (100 iterations) to prevent infinite loops\n */\n\nconst MAX_RECURSION = 100;\n\nexport class UpdateScheduler {\n private _pending = false;\n private _resolve: ((value: boolean) => void) | null = null;\n private _promise: Promise<boolean>;\n private _performUpdate: () => boolean;\n private _recursionCount = 0;\n\n constructor(performUpdate: () => boolean) {\n this._performUpdate = performUpdate;\n this._promise = Promise.resolve(true);\n }\n\n schedule(): void {\n if (this._pending) return;\n this._pending = true;\n\n this._promise = new Promise<boolean>((resolve) => {\n this._resolve = resolve;\n queueMicrotask(() => this._flush(resolve));\n });\n }\n\n private _flush(resolve: (value: boolean) => void): void {\n this._pending = false;\n this._recursionCount++;\n\n if (this._recursionCount > MAX_RECURSION) {\n this._recursionCount = 0;\n resolve(false);\n return;\n }\n\n const success = this._performUpdate();\n\n // If performUpdate triggered another schedule(), the new microtask\n // will handle it. Chain this resolve to the new promise.\n if (this._pending) {\n this._promise.then(resolve);\n } else {\n this._recursionCount = 0;\n resolve(success);\n }\n }\n\n get updateComplete(): Promise<boolean> {\n return this._promise;\n }\n}\n"],"names":[],"mappings":"AAWA,MAAM,gBAAgB;AAEf,MAAM,gBAAgB;AAAA,EAO3B,YAAY,eAA8B;AAN1C,SAAQ,WAAW;AACnB,SAAQ,WAA8C;AAGtD,SAAQ,kBAAkB;AAGxB,SAAK,iBAAiB;AACtB,SAAK,WAAW,QAAQ,QAAQ,IAAI;AAAA,EACtC;AAAA,EAEA,WAAiB;AACf,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAEhB,SAAK,WAAW,IAAI,QAAiB,CAAC,YAAY;AAChD,WAAK,WAAW;AAChB,qBAAe,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,SAAyC;AACtD,SAAK,WAAW;AAChB,SAAK;AAEL,QAAI,KAAK,kBAAkB,eAAe;AACxC,WAAK,kBAAkB;AACvB,cAAQ,KAAK;AACb;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,eAAA;AAIrB,QAAI,KAAK,UAAU;AACjB,WAAK,SAAS,KAAK,OAAO;AAAA,IAC5B,OAAO;AACL,WAAK,kBAAkB;AACvB,cAAQ,OAAO;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,IAAI,iBAAmC;AACrC,WAAO,KAAK;AAAA,EACd;AACF;"}
|
package/dist/model.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { stylesTemplate } from "./utilities/styles.js";
|
|
2
|
+
import { createLogger } from "./utilities/debug.js";
|
|
3
|
+
import { defineObservedAttributes, resolveAttributeConfigs } from "./attributes/config.js";
|
|
4
|
+
import { ChangeDetector } from "./attributes/change-detection.js";
|
|
5
|
+
import { AttributeTypeError, AttributeValidationError } from "./attributes/errors.js";
|
|
6
|
+
import { UpdateScheduler } from "./model/update-cycle.js";
|
|
7
|
+
import { registerComponent } from "./model/registration.js";
|
|
8
|
+
import { getComponentConstructor, isComponentRegistered, registerComponents, whenComponentDefined } from "./model/registration.js";
|
|
9
|
+
import { validateAllSlots, validateSlotElements } from "./slots/slot-validation.js";
|
|
10
|
+
import { createSlotchangeHandler } from "./slots/slot-events.js";
|
|
11
|
+
import { querySlottedElements, querySlottedElement, hasSlottedContent } from "./slots/slot-query.js";
|
|
12
|
+
import { IntersectionController, MediaQueryController } from "./model/controllers.js";
|
|
13
|
+
import { BatchRegistrationError, ComponentRegistrationError } from "./model/errors.js";
|
|
2
14
|
var __create = Object.create;
|
|
3
15
|
var __defProp = Object.defineProperty;
|
|
4
16
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -40,6 +52,17 @@ class BaseComponent extends HTMLElement {
|
|
|
40
52
|
constructor() {
|
|
41
53
|
super();
|
|
42
54
|
this.elementRef = null;
|
|
55
|
+
this._resolvedReactiveAttrs = [];
|
|
56
|
+
this._changeDetector = new ChangeDetector();
|
|
57
|
+
this._reactiveValues = /* @__PURE__ */ new Map();
|
|
58
|
+
this._isReflecting = false;
|
|
59
|
+
this._hasConnected = false;
|
|
60
|
+
this._hasFirstUpdated = false;
|
|
61
|
+
this._changedProperties = /* @__PURE__ */ new Map();
|
|
62
|
+
this._isUpdating = false;
|
|
63
|
+
this._pendingPreUpgrade = null;
|
|
64
|
+
this._slotCleanups = [];
|
|
65
|
+
this._controllers = /* @__PURE__ */ new Set();
|
|
43
66
|
const constructor = this.constructor;
|
|
44
67
|
this.config = constructor.componentConfig;
|
|
45
68
|
if (!this.config) {
|
|
@@ -47,14 +70,24 @@ class BaseComponent extends HTMLElement {
|
|
|
47
70
|
"Component config not found. Did you forget to use the @Component decorator?"
|
|
48
71
|
);
|
|
49
72
|
}
|
|
73
|
+
this._logger = createLogger(this.config.tagName);
|
|
74
|
+
this._updateScheduler = new UpdateScheduler(() => this._performUpdate());
|
|
50
75
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
51
76
|
this.validateConfig();
|
|
77
|
+
this.setupReactiveAttributes();
|
|
52
78
|
}
|
|
53
79
|
static get observedAttributes() {
|
|
54
|
-
|
|
80
|
+
const handlerNames = this.componentConfig?.attributes?.map((attr) => attr.name) || [];
|
|
81
|
+
const reactiveNames = this.componentConfig?.reactiveAttributes ? defineObservedAttributes(this.componentConfig.reactiveAttributes) : [];
|
|
82
|
+
return [.../* @__PURE__ */ new Set([...handlerNames, ...reactiveNames])];
|
|
55
83
|
}
|
|
56
84
|
connectedCallback() {
|
|
57
85
|
try {
|
|
86
|
+
this.applyPreUpgradeProperties();
|
|
87
|
+
if (!this._hasConnected) {
|
|
88
|
+
this._hasConnected = true;
|
|
89
|
+
this.executeFirstConnected();
|
|
90
|
+
}
|
|
58
91
|
this.initializeComponent();
|
|
59
92
|
} catch (error) {
|
|
60
93
|
this.handleError("Failed to initialize component", error);
|
|
@@ -74,6 +107,10 @@ class BaseComponent extends HTMLElement {
|
|
|
74
107
|
initializeComponent() {
|
|
75
108
|
try {
|
|
76
109
|
this.validateSlots();
|
|
110
|
+
if (!this._hasFirstUpdated) {
|
|
111
|
+
this._hasFirstUpdated = true;
|
|
112
|
+
this.executeWillFirstUpdate();
|
|
113
|
+
}
|
|
77
114
|
const componentRef = this.config.createComponent(this);
|
|
78
115
|
if (!componentRef || !componentRef.element) {
|
|
79
116
|
throw new Error(
|
|
@@ -82,6 +119,11 @@ class BaseComponent extends HTMLElement {
|
|
|
82
119
|
}
|
|
83
120
|
this.elementRef = componentRef;
|
|
84
121
|
this.setupShadowDom(componentRef);
|
|
122
|
+
this.setupSlotObservers();
|
|
123
|
+
this.notifyControllers("hostConnected");
|
|
124
|
+
if (this._changedProperties.size > 0) {
|
|
125
|
+
this._updateScheduler.schedule();
|
|
126
|
+
}
|
|
85
127
|
this.afterInit();
|
|
86
128
|
} catch (error) {
|
|
87
129
|
this.handleError("Failed to initialize component", error);
|
|
@@ -125,15 +167,218 @@ class BaseComponent extends HTMLElement {
|
|
|
125
167
|
});
|
|
126
168
|
}
|
|
127
169
|
handleAttributeChange(name, oldValue, newValue) {
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
170
|
+
if (this.elementRef) {
|
|
171
|
+
const handler = this.config.attributes?.find(
|
|
172
|
+
(attr) => attr.name === name
|
|
173
|
+
)?.handler;
|
|
174
|
+
if (handler) {
|
|
175
|
+
handler(this.elementRef, oldValue, newValue);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!this._isReflecting) {
|
|
179
|
+
const resolved = this._resolvedReactiveAttrs.find(
|
|
180
|
+
(r) => r.attributeName === name
|
|
181
|
+
);
|
|
182
|
+
if (resolved) {
|
|
183
|
+
let converted;
|
|
184
|
+
try {
|
|
185
|
+
converted = resolved.converter.fromAttribute(newValue, name);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
throw new AttributeTypeError(
|
|
188
|
+
name,
|
|
189
|
+
resolved.type ?? "unknown",
|
|
190
|
+
newValue,
|
|
191
|
+
this.config.tagName
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const value = converted !== void 0 ? converted : resolved.defaultValue;
|
|
195
|
+
if (value !== void 0 && resolved.validate) {
|
|
196
|
+
const error = resolved.validate(value);
|
|
197
|
+
if (error) {
|
|
198
|
+
throw new AttributeValidationError(name, error, this.config.tagName);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const oldValue2 = this._reactiveValues.get(resolved.propertyName);
|
|
202
|
+
const equalityOverride = resolved.hasChanged ? (prev, next) => !resolved.hasChanged(next, prev) : void 0;
|
|
203
|
+
const changed = this._changeDetector.set(resolved.propertyName, value, equalityOverride);
|
|
204
|
+
this._reactiveValues.set(resolved.propertyName, value);
|
|
205
|
+
if (changed) {
|
|
206
|
+
if (resolved.onChange) {
|
|
207
|
+
resolved.onChange(this, value, oldValue2);
|
|
208
|
+
}
|
|
209
|
+
this.requestUpdate(resolved.propertyName, oldValue2);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Capture a property value set on the instance before upgrade.
|
|
216
|
+
* Deletes the instance property so the class accessor takes precedence,
|
|
217
|
+
* then returns the captured value for re-application.
|
|
218
|
+
*/
|
|
219
|
+
capturePreUpgradeProperty(propertyName) {
|
|
220
|
+
if (this.hasOwnProperty(propertyName)) {
|
|
221
|
+
const value = this[propertyName];
|
|
222
|
+
delete this[propertyName];
|
|
223
|
+
return { value, captured: true };
|
|
224
|
+
}
|
|
225
|
+
return { value: void 0, captured: false };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Apply pre-upgrade property values through reactive setters.
|
|
229
|
+
* Called in connectedCallback after all attributeChangedCallback
|
|
230
|
+
* calls have completed during upgrade.
|
|
231
|
+
*/
|
|
232
|
+
applyPreUpgradeProperties() {
|
|
233
|
+
if (!this._pendingPreUpgrade) return;
|
|
234
|
+
const pending = this._pendingPreUpgrade;
|
|
235
|
+
this._pendingPreUpgrade = null;
|
|
236
|
+
for (const [propertyName, value] of pending) {
|
|
237
|
+
this[propertyName] = value;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
setupReactiveAttributes() {
|
|
241
|
+
if (!this.config.reactiveAttributes) return;
|
|
242
|
+
this._resolvedReactiveAttrs = resolveAttributeConfigs(
|
|
243
|
+
this.config.reactiveAttributes
|
|
244
|
+
);
|
|
245
|
+
for (const resolved of this._resolvedReactiveAttrs) {
|
|
246
|
+
const { propertyName, defaultValue } = resolved;
|
|
247
|
+
const preUpgrade = this.capturePreUpgradeProperty(propertyName);
|
|
248
|
+
if (resolved.attributeName !== false) {
|
|
249
|
+
const raw = this.getAttribute(resolved.attributeName);
|
|
250
|
+
if (raw !== null) {
|
|
251
|
+
const converted = resolved.converter.fromAttribute(
|
|
252
|
+
raw,
|
|
253
|
+
resolved.attributeName
|
|
254
|
+
);
|
|
255
|
+
this._reactiveValues.set(
|
|
256
|
+
propertyName,
|
|
257
|
+
converted !== void 0 ? converted : defaultValue
|
|
258
|
+
);
|
|
259
|
+
} else if (defaultValue !== void 0) {
|
|
260
|
+
this._reactiveValues.set(propertyName, defaultValue);
|
|
261
|
+
}
|
|
262
|
+
} else if (defaultValue !== void 0) {
|
|
263
|
+
this._reactiveValues.set(propertyName, defaultValue);
|
|
264
|
+
}
|
|
265
|
+
Object.defineProperty(this, propertyName, {
|
|
266
|
+
get: () => this._reactiveValues.get(propertyName),
|
|
267
|
+
set: (value) => {
|
|
268
|
+
if (value !== void 0 && resolved.validate) {
|
|
269
|
+
const error = resolved.validate(value);
|
|
270
|
+
if (error) {
|
|
271
|
+
const attrName = resolved.attributeName !== false ? resolved.attributeName : propertyName;
|
|
272
|
+
throw new AttributeValidationError(attrName, error, this.config.tagName);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const oldValue = this._reactiveValues.get(propertyName);
|
|
276
|
+
const equalityOverride = resolved.hasChanged ? (prev, next) => !resolved.hasChanged(next, prev) : void 0;
|
|
277
|
+
const changed = this._changeDetector.set(propertyName, value, equalityOverride);
|
|
278
|
+
if (!changed) return;
|
|
279
|
+
this._reactiveValues.set(propertyName, value);
|
|
280
|
+
if (resolved.reflect && resolved.attributeName !== false) {
|
|
281
|
+
this._isReflecting = true;
|
|
282
|
+
try {
|
|
283
|
+
const attrValue = resolved.converter.toAttribute(value);
|
|
284
|
+
if (attrValue === null) {
|
|
285
|
+
this.removeAttribute(resolved.attributeName);
|
|
286
|
+
} else {
|
|
287
|
+
this.setAttribute(resolved.attributeName, attrValue);
|
|
288
|
+
}
|
|
289
|
+
} finally {
|
|
290
|
+
this._isReflecting = false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (resolved.onChange) {
|
|
294
|
+
resolved.onChange(this, value, oldValue);
|
|
295
|
+
}
|
|
296
|
+
this.requestUpdate(propertyName, oldValue);
|
|
297
|
+
},
|
|
298
|
+
configurable: true,
|
|
299
|
+
enumerable: true
|
|
300
|
+
});
|
|
301
|
+
if (preUpgrade.captured) {
|
|
302
|
+
if (!this._pendingPreUpgrade) {
|
|
303
|
+
this._pendingPreUpgrade = /* @__PURE__ */ new Map();
|
|
304
|
+
}
|
|
305
|
+
this._pendingPreUpgrade.set(propertyName, preUpgrade.value);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
addController(controller) {
|
|
310
|
+
this._controllers.add(controller);
|
|
311
|
+
if (this.isConnected && this.elementRef) {
|
|
312
|
+
controller.hostConnected?.();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
removeController(controller) {
|
|
316
|
+
this._controllers.delete(controller);
|
|
317
|
+
}
|
|
318
|
+
requestUpdate(name, oldValue) {
|
|
319
|
+
if (name !== void 0 && !this._changedProperties.has(name)) {
|
|
320
|
+
this._changedProperties.set(name, oldValue);
|
|
321
|
+
}
|
|
322
|
+
if (!this._hasConnected) return;
|
|
323
|
+
this._updateScheduler.schedule();
|
|
324
|
+
}
|
|
325
|
+
get updateComplete() {
|
|
326
|
+
return this._updateScheduler.updateComplete;
|
|
327
|
+
}
|
|
328
|
+
willUpdate(_changedProperties) {
|
|
329
|
+
this.config.willUpdate?.(this, _changedProperties);
|
|
330
|
+
}
|
|
331
|
+
update(_changedProperties) {
|
|
332
|
+
this.config.update?.(this, _changedProperties);
|
|
333
|
+
}
|
|
334
|
+
updated(_changedProperties) {
|
|
335
|
+
this.config.updated?.(this, _changedProperties);
|
|
336
|
+
}
|
|
337
|
+
_performUpdate() {
|
|
338
|
+
if (this._isUpdating) return true;
|
|
339
|
+
this._isUpdating = true;
|
|
340
|
+
const changedProperties = this._changedProperties;
|
|
341
|
+
this._changedProperties = /* @__PURE__ */ new Map();
|
|
342
|
+
try {
|
|
343
|
+
for (const controller of this._controllers) {
|
|
344
|
+
controller.hostUpdate?.();
|
|
345
|
+
}
|
|
346
|
+
this.willUpdate(changedProperties);
|
|
347
|
+
this.update(changedProperties);
|
|
348
|
+
this.updated(changedProperties);
|
|
349
|
+
for (const controller of this._controllers) {
|
|
350
|
+
controller.hostUpdated?.();
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
this.handleError("Error during update cycle", error);
|
|
355
|
+
return false;
|
|
356
|
+
} finally {
|
|
357
|
+
this._isUpdating = false;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
notifyControllers(method) {
|
|
361
|
+
for (const controller of this._controllers) {
|
|
362
|
+
try {
|
|
363
|
+
controller[method]?.();
|
|
364
|
+
} catch (error) {
|
|
365
|
+
this.handleError(`Controller ${method} failed`, error);
|
|
366
|
+
}
|
|
134
367
|
}
|
|
135
368
|
}
|
|
369
|
+
querySlot(slotName, selector) {
|
|
370
|
+
return querySlottedElements(this, slotName, { selector, flatten: true });
|
|
371
|
+
}
|
|
372
|
+
querySlotElement(slotName, selector) {
|
|
373
|
+
return querySlottedElement(this, slotName, selector);
|
|
374
|
+
}
|
|
375
|
+
hasSlotContent(slotName) {
|
|
376
|
+
return hasSlottedContent(this, slotName);
|
|
377
|
+
}
|
|
136
378
|
cleanup() {
|
|
379
|
+
this.notifyControllers("hostDisconnected");
|
|
380
|
+
for (const fn of this._slotCleanups) fn();
|
|
381
|
+
this._slotCleanups = [];
|
|
137
382
|
this.elementRef = null;
|
|
138
383
|
}
|
|
139
384
|
/**
|
|
@@ -155,7 +400,7 @@ class BaseComponent extends HTMLElement {
|
|
|
155
400
|
);
|
|
156
401
|
}
|
|
157
402
|
handleError(message, error) {
|
|
158
|
-
|
|
403
|
+
this._logger.error(message, error);
|
|
159
404
|
const errorType = this.elementRef ? "runtime" : message.toLowerCase().includes("validation") ? "validation" : "initialization";
|
|
160
405
|
this.dispatchComponentEvent("umdComponent:error", {
|
|
161
406
|
type: errorType,
|
|
@@ -165,55 +410,44 @@ class BaseComponent extends HTMLElement {
|
|
|
165
410
|
}
|
|
166
411
|
validateSlots() {
|
|
167
412
|
if (!this.config.slots) return;
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
if (config.deprecated) {
|
|
182
|
-
const slotElement = this.querySelector(`[slot="${type}"]`);
|
|
183
|
-
if (slotElement) {
|
|
184
|
-
errors.push({
|
|
185
|
-
slot: name,
|
|
186
|
-
error: "deprecated",
|
|
187
|
-
message: `Slot "${type}" is deprecated. ${config.deprecated}`
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (config.allowedElements) {
|
|
192
|
-
const slotElements = Array.from(
|
|
193
|
-
this.querySelectorAll(`[slot="${type}"]`)
|
|
194
|
-
);
|
|
195
|
-
const invalidElements = slotElements.filter(
|
|
196
|
-
(element) => !config.allowedElements.includes(element.tagName.toLowerCase())
|
|
197
|
-
);
|
|
198
|
-
if (invalidElements.length > 0) {
|
|
199
|
-
errors.push({
|
|
200
|
-
slot: name,
|
|
201
|
-
error: "invalid-elements",
|
|
202
|
-
message: `Slot "${type}" contains invalid elements. Allowed: ${config.allowedElements.join(
|
|
203
|
-
", "
|
|
204
|
-
)}`,
|
|
205
|
-
invalidElements
|
|
206
|
-
});
|
|
207
|
-
}
|
|
413
|
+
const result = validateAllSlots(this, this.config.slots);
|
|
414
|
+
this.reportSlotErrors(result);
|
|
415
|
+
}
|
|
416
|
+
reportSlotErrors(result) {
|
|
417
|
+
if (result.isValid) return;
|
|
418
|
+
this.handleError("Slot validation failed", result.errors);
|
|
419
|
+
if (result.errors.length === 1) {
|
|
420
|
+
this._logger.warn(result.errors[0].message);
|
|
421
|
+
} else {
|
|
422
|
+
this._logger.group(`${result.errors.length} slot validation warnings`);
|
|
423
|
+
for (const error of result.errors) {
|
|
424
|
+
this._logger.warn(error.message);
|
|
208
425
|
}
|
|
209
|
-
|
|
210
|
-
if (errors.length > 0) {
|
|
211
|
-
this.handleError("Slot validation failed", errors);
|
|
212
|
-
errors.forEach((error) => {
|
|
213
|
-
console.warn(`[${this.config.tagName}]`, error.message, error);
|
|
214
|
-
});
|
|
426
|
+
this._logger.groupEnd();
|
|
215
427
|
}
|
|
216
428
|
}
|
|
429
|
+
setupSlotObservers() {
|
|
430
|
+
if (!this.config.slots) return;
|
|
431
|
+
const configKeys = Object.keys(this.config.slots);
|
|
432
|
+
const kebabToConfig = /* @__PURE__ */ new Map();
|
|
433
|
+
for (const key of configKeys) {
|
|
434
|
+
const kebab = key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, "");
|
|
435
|
+
kebabToConfig.set(kebab, { key, config: this.config.slots[key] });
|
|
436
|
+
}
|
|
437
|
+
const handler = createSlotchangeHandler(
|
|
438
|
+
this,
|
|
439
|
+
(event) => {
|
|
440
|
+
const slotName = event.slotName ?? "";
|
|
441
|
+
const entry = kebabToConfig.get(slotName);
|
|
442
|
+
if (!entry) return;
|
|
443
|
+
const result = validateSlotElements(entry.key, event.elements, entry.config);
|
|
444
|
+
this.reportSlotErrors(result);
|
|
445
|
+
},
|
|
446
|
+
{ flatten: true }
|
|
447
|
+
);
|
|
448
|
+
handler.connect();
|
|
449
|
+
this._slotCleanups.push(() => handler.disconnect());
|
|
450
|
+
}
|
|
217
451
|
async executeLifecycleCallbacks() {
|
|
218
452
|
const lifecycleMethods = [
|
|
219
453
|
"beforeConnect",
|
|
@@ -238,6 +472,33 @@ class BaseComponent extends HTMLElement {
|
|
|
238
472
|
getRef() {
|
|
239
473
|
return this.elementRef;
|
|
240
474
|
}
|
|
475
|
+
getShadowRoot() {
|
|
476
|
+
return this.shadow;
|
|
477
|
+
}
|
|
478
|
+
executeFirstConnected() {
|
|
479
|
+
try {
|
|
480
|
+
if (this.config.firstConnected) {
|
|
481
|
+
this.config.firstConnected(this, this.shadow);
|
|
482
|
+
}
|
|
483
|
+
this.firstConnected();
|
|
484
|
+
} catch (error) {
|
|
485
|
+
this.handleError("Failed to execute firstConnected hook", error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
firstConnected() {
|
|
489
|
+
}
|
|
490
|
+
executeWillFirstUpdate() {
|
|
491
|
+
try {
|
|
492
|
+
if (this.config.willFirstUpdate) {
|
|
493
|
+
this.config.willFirstUpdate(this, this.shadow);
|
|
494
|
+
}
|
|
495
|
+
this.willFirstUpdate();
|
|
496
|
+
} catch (error) {
|
|
497
|
+
this.handleError("Failed to execute willFirstUpdate hook", error);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
willFirstUpdate() {
|
|
501
|
+
}
|
|
241
502
|
}
|
|
242
503
|
const createCustomElement = (config) => {
|
|
243
504
|
var _Component_decorators, _init, _a;
|
|
@@ -252,8 +513,27 @@ const createCustomElement = (config) => {
|
|
|
252
513
|
__runInitializers(_init, 1, Component);
|
|
253
514
|
return Component;
|
|
254
515
|
};
|
|
516
|
+
const defineComponent = (config, options) => {
|
|
517
|
+
let element = null;
|
|
518
|
+
return () => {
|
|
519
|
+
if (!element) {
|
|
520
|
+
element = createCustomElement(config);
|
|
521
|
+
}
|
|
522
|
+
registerComponent(config.tagName, element, options);
|
|
523
|
+
};
|
|
524
|
+
};
|
|
255
525
|
export {
|
|
256
526
|
BaseComponent,
|
|
257
|
-
|
|
527
|
+
BatchRegistrationError,
|
|
528
|
+
ComponentRegistrationError,
|
|
529
|
+
IntersectionController,
|
|
530
|
+
MediaQueryController,
|
|
531
|
+
createCustomElement,
|
|
532
|
+
defineComponent,
|
|
533
|
+
getComponentConstructor,
|
|
534
|
+
isComponentRegistered,
|
|
535
|
+
registerComponent,
|
|
536
|
+
registerComponents,
|
|
537
|
+
whenComponentDefined
|
|
258
538
|
};
|
|
259
539
|
//# sourceMappingURL=model.js.map
|