@excom/neutron 0.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/.rush/temp/chunked-rush-logs/neutron.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/neutron.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +6 -0
- package/index.ts +9 -0
- package/package.json +45 -0
- package/rush-logs/neutron.apply-exports.cache.log +1 -0
- package/rush-logs/neutron.apply-exports.log +1 -0
- package/rush-logs/neutron.build_package-metas.cache.log +1 -0
- package/rush-logs/neutron.build_package-metas.log +1 -0
- package/src/command.ts +102 -0
- package/src/common-element.ts +377 -0
- package/src/constants.ts +101 -0
- package/src/devtools-hook.ts +93 -0
- package/src/lifecycle-configs.ts +304 -0
- package/src/neutron-element.ts +72 -0
- package/src/neutron-error.ts +6 -0
- package/src/neutron-internal.ts +550 -0
- package/src/neutron.ts +36 -0
- package/src/types/effect.types.ts +104 -0
- package/src/types/element.types.ts +263 -0
- package/src/types/index.ts +4 -0
- package/src/types/new.types.ts +159 -0
- package/src/types/shared.types.ts +25 -0
- package/src/utils/effect.ts +357 -0
- package/src/utils/element.ts +382 -0
- package/src/utils/index.ts +2 -0
- package/support/docs/COMMANDS.md +58 -0
- package/support/docs/COMPOSE.md +32 -0
- package/support/docs/DEBUG.md +11 -0
- package/support/docs/DEFINE.md +20 -0
- package/support/docs/EFFECTS.md +49 -0
- package/support/docs/EVENTS.md +57 -0
- package/support/docs/LIFECYCLES.md +69 -0
- package/support/docs/METHODS.md +49 -0
- package/support/docs/PROMISE_PROPS.md +29 -0
- package/support/docs/PROPS.md +64 -0
- package/support/docs/PROP_REACTIONS.md +35 -0
- package/support/docs/PROVISION.md +31 -0
- package/support/docs/README.md +118 -0
- package/support/docs/RECOMPOSE.md +70 -0
- package/support/docs/TYPESCRIPT.md +51 -0
- package/support/docs-sections.json +42 -0
- package/support/package-meta.json +129 -0
- package/support/tests/commands.test.ts +330 -0
- package/support/tests/common-element.test.ts +342 -0
- package/support/tests/devtools-hook.test.ts +209 -0
- package/support/tests/devtools-renderer.test.ts +125 -0
- package/support/tests/effects.test.ts +253 -0
- package/support/tests/element-config.test.ts +331 -0
- package/support/tests/entry.test.ts +68 -0
- package/support/tests/lifecycles.test.ts +489 -0
- package/support/tests/loop-guard.test.ts +162 -0
- package/support/tests/neutron.test.ts +1286 -0
- package/support/tests/recompose.test.ts +129 -0
- package/support/tests/utils.test.ts +75 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
package/config/rig.json
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./src/command";
|
|
2
|
+
export * from "./src/devtools-hook";
|
|
3
|
+
export * from "./src/neutron";
|
|
4
|
+
export * from "./src/neutron-element";
|
|
5
|
+
export * from "./src/neutron-internal";
|
|
6
|
+
export * from "./src/types";
|
|
7
|
+
export * from "./src/utils";
|
|
8
|
+
// Prop-type marker for space-separated token attributes (runtime value: `string[]`).
|
|
9
|
+
export { TokenList } from "@excom/kit-utils";
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@excom/neutron",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Element factory of the Nucleus Stack — typed custom elements with effect-based lifecycles",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.13.0"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@excom/kit-devtools": "^0.1.0",
|
|
12
|
+
"@excom/kit-logger": "^0.1.0",
|
|
13
|
+
"@excom/kit-utils": "^0.1.0"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@excom/heft-rig": "^0.1.0"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"url": "excom-dev/nucleus",
|
|
21
|
+
"directory": "packages/neutron"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/neutron/support/docs/README.md",
|
|
24
|
+
"bugs": "https://github.com/excom-dev/nucleus/issues",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"Neutron",
|
|
27
|
+
"neutron",
|
|
28
|
+
"kit-element-base",
|
|
29
|
+
"custom-elements",
|
|
30
|
+
"nucleus-stack"
|
|
31
|
+
],
|
|
32
|
+
"excom": {
|
|
33
|
+
"packageType": "element-base"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
|
|
37
|
+
"build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
|
|
38
|
+
"format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
|
|
39
|
+
"test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
|
|
40
|
+
"coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
|
|
41
|
+
"dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
|
|
42
|
+
"preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
|
|
43
|
+
"build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Caching has been disabled for this project's "apply-exports" command.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
package/src/command.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { KitLogger } from "@excom/kit-logger";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `command` event an element receives from the HTML Command API — a
|
|
5
|
+
* `<button command="--verb" commandfor="id">`, `<event-handler
|
|
6
|
+
* command-name="--verb">`, or a script-built `CommandEvent`. It never
|
|
7
|
+
* bubbles, is cancelable and composed. `command` is the verb; `source` is
|
|
8
|
+
* the invoker (the button) or `null`.
|
|
9
|
+
*/
|
|
10
|
+
export type TCommandEvent = Event & {
|
|
11
|
+
readonly command: string;
|
|
12
|
+
readonly source: Element | null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type CommandInit = {
|
|
16
|
+
/** Element that receives the command. Defaults to the dispatching element. */
|
|
17
|
+
target?: Element | null;
|
|
18
|
+
/** Reported as `event.source`. Defaults to the dispatching element. */
|
|
19
|
+
source?: Element | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Arguments of the `command` effect key: `command: ["--fetch", { target }]`. */
|
|
23
|
+
export type CommandArgs = [string, CommandInit?];
|
|
24
|
+
|
|
25
|
+
export const COMMAND_EVENT = "command";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Custom commands are dashed-idents (`--fetch`), like CSS custom
|
|
29
|
+
* properties; the platform reserves every other name for built-ins.
|
|
30
|
+
*/
|
|
31
|
+
export const isCustomCommand = (name: unknown): name is string =>
|
|
32
|
+
typeof name === "string" && /^--\S+$/.test(name);
|
|
33
|
+
|
|
34
|
+
type CommandEventCtor = new (
|
|
35
|
+
type: string,
|
|
36
|
+
init?: EventInit & { command?: string; source?: Element | null }
|
|
37
|
+
) => TCommandEvent;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a `command` event. Uses the platform `CommandEvent` when the
|
|
41
|
+
* browser has one; otherwise a plain `Event` carrying the same two
|
|
42
|
+
* read-only fields, so `onCommand` handlers run identically either way.
|
|
43
|
+
*/
|
|
44
|
+
export function createCommandEvent(
|
|
45
|
+
command: string,
|
|
46
|
+
init: { source?: Element | null } = {}
|
|
47
|
+
): TCommandEvent {
|
|
48
|
+
const source = init.source ?? null;
|
|
49
|
+
const eventInit = { bubbles: false, cancelable: true, composed: true };
|
|
50
|
+
const Ctor = (globalThis as { CommandEvent?: CommandEventCtor }).CommandEvent;
|
|
51
|
+
if (Ctor) {
|
|
52
|
+
return new Ctor(COMMAND_EVENT, { ...eventInit, command, source });
|
|
53
|
+
}
|
|
54
|
+
const event = new Event(COMMAND_EVENT, eventInit) as TCommandEvent;
|
|
55
|
+
Object.defineProperties(event, {
|
|
56
|
+
command: { value: command, enumerable: true },
|
|
57
|
+
source: { value: source, enumerable: true },
|
|
58
|
+
});
|
|
59
|
+
return event;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Invoke `command` on `target`. A custom command (`--verb`) is dispatched
|
|
64
|
+
* directly and the event is returned. A built-in verb (`show-modal`,
|
|
65
|
+
* `toggle-popover`, …) can only run through the platform: an invisible
|
|
66
|
+
* proxy `<button command commandfor>` is clicked and removed, and `null`
|
|
67
|
+
* is returned (browsers without the Command API log a warning and do
|
|
68
|
+
* nothing).
|
|
69
|
+
*/
|
|
70
|
+
export function invokeCommand(
|
|
71
|
+
target: Element,
|
|
72
|
+
command: string,
|
|
73
|
+
source: Element | null = null
|
|
74
|
+
): TCommandEvent | null {
|
|
75
|
+
if (isCustomCommand(command)) {
|
|
76
|
+
const event = createCommandEvent(command, { source });
|
|
77
|
+
target.dispatchEvent(event);
|
|
78
|
+
return event;
|
|
79
|
+
}
|
|
80
|
+
const proto = HTMLButtonElement.prototype as { commandForElement?: unknown };
|
|
81
|
+
if (!("commandForElement" in proto)) {
|
|
82
|
+
KitLogger.warn(
|
|
83
|
+
`Command "${command}" needs the HTML Command API (\`commandfor\`), which this browser lacks. Custom commands (\`--name\`) work everywhere.`
|
|
84
|
+
);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const proxy = target.ownerDocument.createElement(
|
|
88
|
+
"button"
|
|
89
|
+
) as HTMLButtonElement & { commandForElement: Element; command: string };
|
|
90
|
+
// No attribute writes here: test complexity meters count `setAttribute`.
|
|
91
|
+
// Detached from any form, the proxy cannot submit, so `type` can stay.
|
|
92
|
+
proxy.style.display = "none";
|
|
93
|
+
target.ownerDocument.body.appendChild(proxy);
|
|
94
|
+
proxy.commandForElement = target;
|
|
95
|
+
proxy.command = command;
|
|
96
|
+
try {
|
|
97
|
+
proxy.click();
|
|
98
|
+
} finally {
|
|
99
|
+
proxy.remove();
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { CommandArgs, invokeCommand } from "./command";
|
|
2
|
+
import type { NeutronElement as TNeutronElement } from "./neutron-element";
|
|
3
|
+
import type { NeutronInternal as TNeutronInternal } from "./neutron-internal";
|
|
4
|
+
import type {
|
|
5
|
+
EmitArgs,
|
|
6
|
+
NativeNElement,
|
|
7
|
+
RuntimeConfig,
|
|
8
|
+
SuperAddEventListenerOptions,
|
|
9
|
+
} from "./types";
|
|
10
|
+
import { KitLogger } from "@excom/kit-logger";
|
|
11
|
+
|
|
12
|
+
const findExistingListenerIndex = (listenerArray, type, fn) => {
|
|
13
|
+
return listenerArray.findIndex(([t, f]) => t === type && f === fn);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const deref = <T extends HTMLElement | Node | EventTarget>(
|
|
17
|
+
el: T | WeakRef<T>
|
|
18
|
+
): T | undefined => {
|
|
19
|
+
return el instanceof WeakRef ? el.deref() : el;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getNamespace = (
|
|
23
|
+
_element: NativeNElement | HTMLElement | TNeutronElement
|
|
24
|
+
): NativeNElement["_n_"] => {
|
|
25
|
+
const element = _element as NativeNElement;
|
|
26
|
+
if (!element._n_) {
|
|
27
|
+
element._n_ = {
|
|
28
|
+
element: element,
|
|
29
|
+
eventListeners: [],
|
|
30
|
+
disconnectedEventListeners: [],
|
|
31
|
+
disconnectedBroadcastListeners: [],
|
|
32
|
+
broadcastListeners: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (!element._n_.eventListeners) {
|
|
36
|
+
element._n_.eventListeners = [];
|
|
37
|
+
}
|
|
38
|
+
if (!element._n_.broadcastListeners) {
|
|
39
|
+
element._n_.broadcastListeners = [];
|
|
40
|
+
}
|
|
41
|
+
return element._n_;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const buildEventType = (
|
|
45
|
+
type: string,
|
|
46
|
+
ctr: typeof TNeutronInternal | undefined,
|
|
47
|
+
opts: OptsWithInternal = { _broadcast: false }
|
|
48
|
+
): string => {
|
|
49
|
+
const config = ctr?.runtimeConfig as RuntimeConfig;
|
|
50
|
+
if (!opts._broadcast && config?.events?.[type]?.prefixWithTag) {
|
|
51
|
+
return `${config.tag}-${type}`;
|
|
52
|
+
} else if (opts._broadcast && config?.broadcasts?.[type]?.prefixWithTag) {
|
|
53
|
+
return `${config.tag}-${type}`;
|
|
54
|
+
}
|
|
55
|
+
return type;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const BROADCAST_CHANNEL = new EventTarget();
|
|
59
|
+
|
|
60
|
+
interface OptsWithInternal extends SuperAddEventListenerOptions {
|
|
61
|
+
_broadcast?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const CommonElement = {
|
|
65
|
+
addListener(_type, fn, _opts: OptsWithInternal = { _broadcast: false }) {
|
|
66
|
+
const { element, ctr } = getNamespace(this);
|
|
67
|
+
const type = buildEventType(_type, ctr, _opts);
|
|
68
|
+
|
|
69
|
+
// TODO clean up this inconsistent _parent args approach
|
|
70
|
+
const { _broadcast, target, ...opts } = _opts;
|
|
71
|
+
const listenerArray = _broadcast
|
|
72
|
+
? element._n_.broadcastListeners
|
|
73
|
+
: element._n_.eventListeners;
|
|
74
|
+
const foundIndex = findExistingListenerIndex(listenerArray, type, fn);
|
|
75
|
+
const host = _broadcast ? BROADCAST_CHANNEL : target || element;
|
|
76
|
+
if (foundIndex < 0) {
|
|
77
|
+
// first registration of this (type, fn)
|
|
78
|
+
host.addEventListener.apply(host, [type, fn, opts]);
|
|
79
|
+
listenerArray.push([
|
|
80
|
+
type,
|
|
81
|
+
fn,
|
|
82
|
+
// @ts-ignore fix weakref typing here
|
|
83
|
+
{
|
|
84
|
+
...opts,
|
|
85
|
+
...(target ? { target: new WeakRef(target) } : {}),
|
|
86
|
+
},
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
// same fn already listed: leave it
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
addBroadcastListener(type, fn, opts: SuperAddEventListenerOptions = {}) {
|
|
93
|
+
CommonElement.addListener.apply(this, [
|
|
94
|
+
type,
|
|
95
|
+
fn,
|
|
96
|
+
{ ...opts, _broadcast: true },
|
|
97
|
+
]);
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
removeListener(_type, fn, _opts: OptsWithInternal = { _broadcast: false }) {
|
|
101
|
+
const { element, ctr } = getNamespace(this);
|
|
102
|
+
const type = buildEventType(_type, ctr, _opts);
|
|
103
|
+
|
|
104
|
+
const { _broadcast } = _opts;
|
|
105
|
+
|
|
106
|
+
const listenerArray = _broadcast
|
|
107
|
+
? element._n_.broadcastListeners
|
|
108
|
+
: element._n_.eventListeners;
|
|
109
|
+
const foundIndex = findExistingListenerIndex(listenerArray, type, fn);
|
|
110
|
+
if (foundIndex > -1) {
|
|
111
|
+
const listenerArgsToRemove = listenerArray[foundIndex];
|
|
112
|
+
if (!_broadcast) {
|
|
113
|
+
const host = _opts.target || element;
|
|
114
|
+
host.removeEventListener.apply(host, listenerArgsToRemove);
|
|
115
|
+
element._n_.eventListeners = element._n_.eventListeners.filter(
|
|
116
|
+
([t, f]) =>
|
|
117
|
+
!(t === listenerArgsToRemove[0] && f === listenerArgsToRemove[1])
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
BROADCAST_CHANNEL.removeEventListener.apply(
|
|
121
|
+
BROADCAST_CHANNEL,
|
|
122
|
+
listenerArgsToRemove
|
|
123
|
+
);
|
|
124
|
+
element._n_.broadcastListeners = element._n_.broadcastListeners.filter(
|
|
125
|
+
([t, f]) =>
|
|
126
|
+
!(t === listenerArgsToRemove[0] && f === listenerArgsToRemove[1])
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
removeBroadcastListener(type, fn, opts: SuperAddEventListenerOptions = {}) {
|
|
133
|
+
CommonElement.removeListener.apply(this, [
|
|
134
|
+
type,
|
|
135
|
+
fn,
|
|
136
|
+
{ ...opts, _broadcast: true },
|
|
137
|
+
]);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
addListeners(...args: [string, Function, OptsWithInternal][]) {
|
|
141
|
+
args.forEach(([type, listener, options]) =>
|
|
142
|
+
CommonElement.addListener.apply(this, [type, listener, options])
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
addBroadcastListeners(
|
|
147
|
+
...args: [string, Function, SuperAddEventListenerOptions][]
|
|
148
|
+
) {
|
|
149
|
+
args.forEach(([type, listener, options]) =>
|
|
150
|
+
CommonElement.addListener.apply(this, [
|
|
151
|
+
type,
|
|
152
|
+
listener,
|
|
153
|
+
{ ...(options || {}), _broadcast: true },
|
|
154
|
+
])
|
|
155
|
+
);
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
removeListeners(...args: [string, Function, OptsWithInternal][]) {
|
|
159
|
+
args.forEach((params) => CommonElement.removeListener.apply(this, params));
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
removeBroadcastListeners(
|
|
163
|
+
...args: [string, Function, SuperAddEventListenerOptions][]
|
|
164
|
+
) {
|
|
165
|
+
args.forEach(([type, listener, options]) =>
|
|
166
|
+
CommonElement.removeListener.apply(this, [
|
|
167
|
+
type,
|
|
168
|
+
listener,
|
|
169
|
+
{ ...(options || {}), _broadcast: true },
|
|
170
|
+
])
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
toggleListeners(...args: [string, Function, boolean, OptsWithInternal][]) {
|
|
175
|
+
args.forEach(([type, listener, toggle, options]) => {
|
|
176
|
+
const action = toggle ? "addListener" : "removeListener";
|
|
177
|
+
CommonElement[action].apply(this, [type, listener, options]);
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
toggleBroadcastListeners(
|
|
182
|
+
...args: [string, Function, boolean, SuperAddEventListenerOptions][]
|
|
183
|
+
) {
|
|
184
|
+
args.forEach(([type, listener, toggle, options]) => {
|
|
185
|
+
const action = toggle ? "addListener" : "removeListener";
|
|
186
|
+
CommonElement[action].apply(this, [
|
|
187
|
+
type,
|
|
188
|
+
listener,
|
|
189
|
+
{ ...(options || {}), _broadcast: true },
|
|
190
|
+
]);
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
removeAllListeners(opts = { _broadcast: false }) {
|
|
195
|
+
const { element } = getNamespace(this);
|
|
196
|
+
if (!opts?._broadcast) {
|
|
197
|
+
element._n_.eventListeners
|
|
198
|
+
?.filter((params) =>
|
|
199
|
+
params?.[2]?.target ? !!deref(params[2].target) : true
|
|
200
|
+
)
|
|
201
|
+
?.forEach((params) =>
|
|
202
|
+
CommonElement.removeListener.apply(element, [
|
|
203
|
+
params[0],
|
|
204
|
+
params[1],
|
|
205
|
+
{
|
|
206
|
+
...params[2],
|
|
207
|
+
...(params[2]?.target ? { target: deref(params[2].target) } : {}),
|
|
208
|
+
},
|
|
209
|
+
])
|
|
210
|
+
);
|
|
211
|
+
} else {
|
|
212
|
+
element._n_.broadcastListeners?.forEach((params) =>
|
|
213
|
+
CommonElement.removeListener.apply(element, [
|
|
214
|
+
params[0],
|
|
215
|
+
params[1],
|
|
216
|
+
{ ...(params[2] || {}), _broadcast: true },
|
|
217
|
+
])
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
removeAllBroadcastListeners() {
|
|
223
|
+
CommonElement.removeAllListeners.apply(this, [{ _broadcast: true }]);
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
internal_disconnectEventListeners(opts = { _broadcast: false }) {
|
|
227
|
+
const { element } = getNamespace(this);
|
|
228
|
+
if (!opts?._broadcast) {
|
|
229
|
+
element._n_.disconnectedEventListeners = element._n_.eventListeners || [];
|
|
230
|
+
element._n_.eventListeners
|
|
231
|
+
?.filter((params) =>
|
|
232
|
+
params?.[2]?.target ? !!deref(params[2].target) : true
|
|
233
|
+
)
|
|
234
|
+
?.forEach((params) =>
|
|
235
|
+
CommonElement.removeListener.apply(element, [
|
|
236
|
+
params[0],
|
|
237
|
+
params[1],
|
|
238
|
+
{
|
|
239
|
+
...params[2],
|
|
240
|
+
...(params[2]?.target ? { target: deref(params[2].target) } : {}),
|
|
241
|
+
},
|
|
242
|
+
])
|
|
243
|
+
);
|
|
244
|
+
} else {
|
|
245
|
+
element._n_.disconnectedBroadcastListeners =
|
|
246
|
+
element._n_.broadcastListeners || [];
|
|
247
|
+
element._n_.broadcastListeners?.forEach((params) =>
|
|
248
|
+
CommonElement.removeListener.apply(element, [
|
|
249
|
+
params[0],
|
|
250
|
+
params[1],
|
|
251
|
+
{ ...(params[2] || {}), _broadcast: true },
|
|
252
|
+
])
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
internal_disconnectBroadcastListeners(opts = {}) {
|
|
257
|
+
CommonElement.internal_disconnectEventListeners.apply(this, [
|
|
258
|
+
{ ...opts, _broadcast: true },
|
|
259
|
+
]);
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
internal_reconnectEventListeners(opts = { _broadcast: false }) {
|
|
263
|
+
const { element } = getNamespace(this);
|
|
264
|
+
if (!opts?._broadcast) {
|
|
265
|
+
element._n_.disconnectedEventListeners
|
|
266
|
+
// `once` is not reconnected: we cannot tell if it already fired
|
|
267
|
+
?.filter(
|
|
268
|
+
(params) =>
|
|
269
|
+
!params?.[2]?.once &&
|
|
270
|
+
(params?.[2]?.target ? !!deref(params[2].target) : true)
|
|
271
|
+
)
|
|
272
|
+
?.forEach((params) =>
|
|
273
|
+
CommonElement.addListener.apply(element, [
|
|
274
|
+
params[0],
|
|
275
|
+
params[1],
|
|
276
|
+
{
|
|
277
|
+
...params[2],
|
|
278
|
+
...(params[2]?.target ? { target: deref(params[2].target) } : {}),
|
|
279
|
+
},
|
|
280
|
+
])
|
|
281
|
+
);
|
|
282
|
+
element._n_.disconnectedEventListeners = [];
|
|
283
|
+
} else {
|
|
284
|
+
element._n_.disconnectedBroadcastListeners
|
|
285
|
+
?.filter((params) => !params?.[2]?.once)
|
|
286
|
+
?.forEach((params) =>
|
|
287
|
+
CommonElement.addListener.apply(element, [
|
|
288
|
+
params[0],
|
|
289
|
+
params[1],
|
|
290
|
+
{ ...(params[2] || {}), _broadcast: true },
|
|
291
|
+
])
|
|
292
|
+
);
|
|
293
|
+
element._n_.disconnectedBroadcastListeners = [];
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
internal_reconnectBroadcastListeners(opts = {}) {
|
|
297
|
+
CommonElement.internal_reconnectEventListeners.apply(this, [
|
|
298
|
+
{ ...opts, _broadcast: true },
|
|
299
|
+
]);
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
emit(
|
|
303
|
+
_type: EmitArgs[0],
|
|
304
|
+
eventInitObj: EmitArgs[1] = {},
|
|
305
|
+
opts = { _broadcast: false }
|
|
306
|
+
) {
|
|
307
|
+
const { target, ...eventInit } = eventInitObj;
|
|
308
|
+
const el = target || this;
|
|
309
|
+
if (!_type || !el) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`CommonElement.emit: Event target and type are required.`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const { element, ctr } = getNamespace(el);
|
|
315
|
+
const type = buildEventType(_type, ctr, opts);
|
|
316
|
+
|
|
317
|
+
const event = new CustomEvent(type, {
|
|
318
|
+
bubbles: true,
|
|
319
|
+
cancelable: true,
|
|
320
|
+
composed: true,
|
|
321
|
+
...eventInit,
|
|
322
|
+
});
|
|
323
|
+
if (!element.isConnected) {
|
|
324
|
+
KitLogger.warn(
|
|
325
|
+
`CommonElement.emit: Element is not connected: "${
|
|
326
|
+
element.localName
|
|
327
|
+
}". Dispatched ${
|
|
328
|
+
opts._broadcast ? "broadcast" : "event"
|
|
329
|
+
} "${type}". This means your element is likely incorrectly emitting events either before connection (possibly eager prop handlers or firing event in .onConstructed()) or after disconnection (possibly due to a lack of cleanup code in .onDisconnected()). Failure to fix this issue may lead to memory leaks or unexpected behavior.`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
(opts._broadcast ? BROADCAST_CHANNEL : element).dispatchEvent(event);
|
|
333
|
+
return event;
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
emits(...eventConfigs) {
|
|
337
|
+
const { element } = getNamespace(this);
|
|
338
|
+
return eventConfigs.map((conf) => CommonElement.emit.apply(element, conf));
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
broadcast(type, eventInitObj = {}) {
|
|
342
|
+
CommonElement.emit.apply(this, [
|
|
343
|
+
type,
|
|
344
|
+
{ ...eventInitObj, bubbles: false },
|
|
345
|
+
{ _broadcast: true },
|
|
346
|
+
]);
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
broadcasts(...eventConfigs) {
|
|
350
|
+
const { element } = getNamespace(this);
|
|
351
|
+
return eventConfigs.map((conf) =>
|
|
352
|
+
CommonElement.broadcast.apply(element, conf)
|
|
353
|
+
);
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
/*
|
|
357
|
+
* Invoke a command: `command: ["--fetch", { target }]`. The element is
|
|
358
|
+
* the `source` (and the target, when none is given). A `--` command
|
|
359
|
+
* dispatches a `command` event; a built-in verb goes through a proxy
|
|
360
|
+
* button (see `invokeCommand`).
|
|
361
|
+
*/
|
|
362
|
+
command(name: CommandArgs[0], init: CommandArgs[1] = {}) {
|
|
363
|
+
const { element } = getNamespace(this);
|
|
364
|
+
const target = init.target || element;
|
|
365
|
+
const source = init.source === undefined ? element : init.source;
|
|
366
|
+
return invokeCommand(target, name, source);
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
commands(...commandConfigs: CommandArgs[]) {
|
|
370
|
+
const { element } = getNamespace(this);
|
|
371
|
+
return commandConfigs.map((conf) =>
|
|
372
|
+
CommonElement.command.apply(element, conf)
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export { CommonElement };
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { CommonElement } from "./common-element";
|
|
2
|
+
import type { DefaultConfig } from "./types";
|
|
3
|
+
import { camelToDash } from "@excom/kit-utils";
|
|
4
|
+
|
|
5
|
+
const ALWAYS_IGNORE = [undefined, NaN, 0n];
|
|
6
|
+
export const IGNORED_STR_VALUES = [false, 0, ...ALWAYS_IGNORE];
|
|
7
|
+
export const IGNORED_NUM_VALUES = [false, "", ...ALWAYS_IGNORE];
|
|
8
|
+
export const IGNORED_BOOL_VALUES = [0, "", ...ALWAYS_IGNORE];
|
|
9
|
+
export const IGNORED_PROP_VALUES = [0, false, "", ...ALWAYS_IGNORE];
|
|
10
|
+
export const IGNORED_FUNC_VALUES = [null, ...IGNORED_PROP_VALUES];
|
|
11
|
+
export const IGNORED_EFFECTOR_RESULT = [null, ...IGNORED_PROP_VALUES];
|
|
12
|
+
export type IgnoredEffectorResult = void | undefined | null | false | "" | 0;
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_CONFIG: DefaultConfig = {
|
|
15
|
+
props: {
|
|
16
|
+
isMounted: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
notify: false,
|
|
19
|
+
attr: false,
|
|
20
|
+
},
|
|
21
|
+
isAdopted: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
notify: false,
|
|
24
|
+
attr: false,
|
|
25
|
+
},
|
|
26
|
+
wasMounted: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
notify: false,
|
|
29
|
+
attr: false,
|
|
30
|
+
},
|
|
31
|
+
isMoving: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
notify: false,
|
|
34
|
+
attr: false,
|
|
35
|
+
},
|
|
36
|
+
renderRoot: {
|
|
37
|
+
type: HTMLElement,
|
|
38
|
+
notify: false,
|
|
39
|
+
attr: false,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const PROTECTED_PROP_NAMES = [
|
|
45
|
+
// NeutronInternal statics
|
|
46
|
+
"builtConfig",
|
|
47
|
+
"runtimeConfig",
|
|
48
|
+
"connected",
|
|
49
|
+
"constructed",
|
|
50
|
+
"adopted",
|
|
51
|
+
"error",
|
|
52
|
+
"propSet",
|
|
53
|
+
"propUnset",
|
|
54
|
+
"promiseResolved",
|
|
55
|
+
"promiseRejected",
|
|
56
|
+
"disconnected",
|
|
57
|
+
"broadcast",
|
|
58
|
+
"event",
|
|
59
|
+
"eventDefault",
|
|
60
|
+
"command",
|
|
61
|
+
"NeutronElement",
|
|
62
|
+
"CustomElement",
|
|
63
|
+
// NeutronElement instances
|
|
64
|
+
"element",
|
|
65
|
+
"ctr",
|
|
66
|
+
"eventListeners",
|
|
67
|
+
"disconnectedEventListeners",
|
|
68
|
+
"broadcastListeners",
|
|
69
|
+
"disconnectedBroadcastListeners",
|
|
70
|
+
"props",
|
|
71
|
+
"propStore",
|
|
72
|
+
"queueManager",
|
|
73
|
+
"debug",
|
|
74
|
+
// CommonElement keys
|
|
75
|
+
...Object.keys(CommonElement),
|
|
76
|
+
// custom-element statics
|
|
77
|
+
"observedAttributes",
|
|
78
|
+
"NeutronInternal",
|
|
79
|
+
// custom-element instances
|
|
80
|
+
"_n_",
|
|
81
|
+
"_q_",
|
|
82
|
+
"connectedCallback",
|
|
83
|
+
"adoptedCallback",
|
|
84
|
+
"disconnectedCallback",
|
|
85
|
+
"attributeChangedCallback",
|
|
86
|
+
...Object.keys(DEFAULT_CONFIG.props),
|
|
87
|
+
// Other
|
|
88
|
+
"returns",
|
|
89
|
+
// `content` is too generic; Quark and CSS already use it for children.
|
|
90
|
+
"content",
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
export const PROTECTED_ATTR_NAMES = [
|
|
94
|
+
/^q-/,
|
|
95
|
+
/^n-/,
|
|
96
|
+
/^on-/,
|
|
97
|
+
/^off-/,
|
|
98
|
+
...PROTECTED_PROP_NAMES.map((p) => new RegExp(`^${camelToDash(p)}$`)),
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
export const PROPS_TO_DEEP_MERGE = ["style"];
|