@stacksjs/hello-world-stx 0.75.329 → 0.75.334

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.
Files changed (37) hide show
  1. package/README.md +7 -0
  2. package/dist/bundle.js +474 -0
  3. package/dist/bundle.js.map +23 -0
  4. package/dist/custom-elements.d.ts +153 -0
  5. package/dist/custom-elements.json +927 -0
  6. package/dist/index.d.ts +13 -0
  7. package/dist/index.js +13 -0
  8. package/dist/runtime.js +309 -0
  9. package/dist/ssr.d.ts +27 -0
  10. package/dist/ssr.js +388 -0
  11. package/dist/stacks-context-menu-item.d.ts +27 -0
  12. package/dist/stacks-context-menu-item.js +85 -0
  13. package/dist/stacks-context-menu.d.ts +24 -0
  14. package/dist/stacks-context-menu.js +35 -0
  15. package/dist/stacks-desktop-icon.d.ts +33 -0
  16. package/dist/stacks-desktop-icon.js +114 -0
  17. package/dist/stacks-feature-button.d.ts +26 -0
  18. package/dist/stacks-feature-button.js +56 -0
  19. package/dist/stacks-input-group.d.ts +28 -0
  20. package/dist/stacks-input-group.js +68 -0
  21. package/dist/stacks-login-form.d.ts +28 -0
  22. package/dist/stacks-login-form.js +85 -0
  23. package/dist/stacks-login-screen.d.ts +26 -0
  24. package/dist/stacks-login-screen.js +77 -0
  25. package/dist/stacks-notification-popup.d.ts +26 -0
  26. package/dist/stacks-notification-popup.js +85 -0
  27. package/dist/stacks-package-item.d.ts +26 -0
  28. package/dist/stacks-package-item.js +49 -0
  29. package/dist/stacks-start-menu-item.d.ts +28 -0
  30. package/dist/stacks-start-menu-item.js +99 -0
  31. package/dist/stacks-start-menu.d.ts +26 -0
  32. package/dist/stacks-start-menu.js +85 -0
  33. package/dist/stacks-taskbar.d.ts +24 -0
  34. package/dist/stacks-taskbar.js +63 -0
  35. package/dist/stacks-window.d.ts +31 -0
  36. package/dist/stacks-window.js +104 -0
  37. package/package.json +4 -4
@@ -0,0 +1,13 @@
1
+ export * from './stacks-context-menu.js'
2
+ export * from './stacks-context-menu-item.js'
3
+ export * from './stacks-desktop-icon.js'
4
+ export * from './stacks-feature-button.js'
5
+ export * from './stacks-input-group.js'
6
+ export * from './stacks-login-form.js'
7
+ export * from './stacks-login-screen.js'
8
+ export * from './stacks-notification-popup.js'
9
+ export * from './stacks-package-item.js'
10
+ export * from './stacks-start-menu.js'
11
+ export * from './stacks-start-menu-item.js'
12
+ export * from './stacks-taskbar.js'
13
+ export * from './stacks-window.js'
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export { ContextMenu, default as ContextMenuElement } from './stacks-context-menu.js';
2
+ export { ContextMenuItem, default as ContextMenuItemElement } from './stacks-context-menu-item.js';
3
+ export { DesktopIcon, default as DesktopIconElement } from './stacks-desktop-icon.js';
4
+ export { FeatureButton, default as FeatureButtonElement } from './stacks-feature-button.js';
5
+ export { InputGroup, default as InputGroupElement } from './stacks-input-group.js';
6
+ export { LoginForm, default as LoginFormElement } from './stacks-login-form.js';
7
+ export { LoginScreen, default as LoginScreenElement } from './stacks-login-screen.js';
8
+ export { NotificationPopup, default as NotificationPopupElement } from './stacks-notification-popup.js';
9
+ export { PackageItem, default as PackageItemElement } from './stacks-package-item.js';
10
+ export { StartMenu, default as StartMenuElement } from './stacks-start-menu.js';
11
+ export { StartMenuItem, default as StartMenuItemElement } from './stacks-start-menu-item.js';
12
+ export { Taskbar, default as TaskbarElement } from './stacks-taskbar.js';
13
+ export { Window, default as WindowElement } from './stacks-window.js';
@@ -0,0 +1,309 @@
1
+ const HTMLElementBase = globalThis.HTMLElement || class {};
2
+
3
+ const escapeHTML = (value) => String(value ?? '')
4
+ .replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
5
+ .replaceAll('"', '&quot;').replaceAll("'", '&#039;');
6
+
7
+ const valueAt = (host, path) => {
8
+ const parts = path.trim().split('.');
9
+ const first = parts.shift();
10
+ // Render bindings read declared props, including attributes whose names
11
+ // also belong to the native DOM API. The public element keeps that API.
12
+ let value = host._values && Object.prototype.hasOwnProperty.call(host._values, first)
13
+ ? host._values[first]
14
+ : host[first];
15
+ for (const part of parts) value = value?.[part];
16
+ return typeof value === 'function' ? value.call(host) : value;
17
+ };
18
+
19
+ const deserialize = (value, type, fallback) => {
20
+ if (value === null) return type === 'boolean' ? false : fallback;
21
+ if (type === 'boolean') return value !== 'false' && value !== '0';
22
+ if (type === 'number') return Number(value);
23
+ if (type === 'object' || type === 'array') {
24
+ try { return JSON.parse(value); }
25
+ catch { return fallback; }
26
+ }
27
+ return value;
28
+ };
29
+
30
+ const serialize = (value, type) => {
31
+ if (value == null || value === false) return null;
32
+ if (type === 'boolean') return '';
33
+ if (type === 'object' || type === 'array') return JSON.stringify(value);
34
+ return String(value);
35
+ };
36
+
37
+ const cloneDefault = (value, type) => {
38
+ if (value == null || (type !== 'object' && type !== 'array')) return value;
39
+ if (typeof structuredClone === 'function') return structuredClone(value);
40
+ return JSON.parse(JSON.stringify(value));
41
+ };
42
+
43
+ const defaultFor = property => cloneDefault(
44
+ property.default ?? ({ string: '', number: 0, boolean: false, object: {}, array: [] })[property.type],
45
+ property.type,
46
+ );
47
+
48
+ export class StxElement extends HTMLElementBase {
49
+ static definition = {};
50
+ static get observedAttributes() { return this.definition.observedAttributes || []; }
51
+
52
+ constructor() {
53
+ super();
54
+ this._values = Object.create(null);
55
+ this._reflecting = false;
56
+ this._connected = false;
57
+ this._hasConnected = false;
58
+ this._initializing = false;
59
+ this._updatePending = false;
60
+ this._listeners = new Map();
61
+ const definition = this.constructor.definition;
62
+ for (const [name, property] of Object.entries(definition.properties || {})) {
63
+ this._values[name] = defaultFor(property);
64
+ }
65
+ if (definition.shadowMode && this.attachShadow && !this.shadowRoot) {
66
+ this._closedRoot = this.attachShadow({ mode: definition.shadowMode });
67
+ }
68
+ }
69
+
70
+ get updateComplete() { return this._updateComplete || Promise.resolve(); }
71
+ get renderRoot() { return this._closedRoot || this.shadowRoot || this; }
72
+
73
+ connectedCallback() {
74
+ if (this._connected) return;
75
+ this._connected = true;
76
+ this._initializing = true;
77
+ const definition = this.constructor.definition;
78
+ for (const [name, property] of Object.entries(definition.properties || {})) {
79
+ const attribute = property.attribute === false ? null : (property.attribute || name.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase()));
80
+ if (attribute && this.hasAttribute?.(attribute)) this._values[name] = deserialize(this.getAttribute(attribute), property.type, defaultFor(property));
81
+ }
82
+ // Properties assigned before customElements.define() become own properties.
83
+ // Upgrade them after attributes so imperative state wins, matching the
84
+ // platform's property-first custom-element initialization convention.
85
+ for (const name of Object.keys(definition.properties || {})) this._upgradeProperty(name);
86
+ this._initializing = false;
87
+ const root = this.renderRoot;
88
+ const hasMarkup = [...(root.childNodes || [])].some(node => node.nodeType !== 3 || node.textContent.trim());
89
+ const firstConnection = !this._hasConnected;
90
+ if (!(firstConnection && definition.progressive && hasMarkup)) this._render();
91
+ else this._upgradeDeclarativeEvents();
92
+ this._bindEvents();
93
+ if (firstConnection) {
94
+ this._hasConnected = true;
95
+ this.setAttribute?.('hydrated', '');
96
+ this.dispatchEvent?.(new CustomEvent('stx:hydrated', { bubbles: true, composed: true }));
97
+ }
98
+ // Called last, so a component that acquires something sees its own rendered
99
+ // markup and its events already bound. Reported rather than rethrown: a
100
+ // component that fails to set up must not take the element down with it.
101
+ try { this.onConnect?.(); }
102
+ catch (error) { console.error('[stx] onConnect failed', error); }
103
+ }
104
+
105
+ disconnectedCallback() {
106
+ this._connected = false;
107
+ // Called before the framework releases anything, so the element is still
108
+ // intact for a component that needs to read it on the way out, and inside
109
+ // try/finally so a throwing hook cannot leak the framework's own listeners.
110
+ try { this.onDisconnect?.(); }
111
+ catch (error) { console.error('[stx] onDisconnect failed', error); }
112
+ finally {
113
+ for (const [event, listener] of this._listeners) this.renderRoot.removeEventListener(event, listener);
114
+ this._listeners.clear();
115
+ }
116
+ }
117
+
118
+ attributeChangedCallback(attribute, oldValue, newValue) {
119
+ if (oldValue === newValue || this._reflecting) return;
120
+ const definition = this.constructor.definition;
121
+ const name = definition.attributeToProperty?.[attribute];
122
+ if (!name) return;
123
+ const property = definition.properties[name];
124
+ const value = deserialize(newValue, property.type, defaultFor(property));
125
+ if (!Object.is(this._values[name], value)) {
126
+ this._values[name] = value;
127
+ this.requestUpdate();
128
+ }
129
+ }
130
+
131
+ /**
132
+ * The props a compiled render reads.
133
+ *
134
+ * Its own method rather than _values directly, so a component that sets a
135
+ * property before upgrade still sees it, and so the generated code has one
136
+ * name to depend on rather than a private field's shape.
137
+ */
138
+ _props() {
139
+ return this._values;
140
+ }
141
+
142
+ requestUpdate() {
143
+ if (!this._connected || this._initializing || this._updatePending) return this.updateComplete;
144
+ this._updatePending = true;
145
+ this._updateComplete = new Promise((resolve) => {
146
+ queueMicrotask(() => {
147
+ this._updatePending = false;
148
+ if (this._connected) this._render();
149
+ resolve();
150
+ });
151
+ });
152
+ return this._updateComplete;
153
+ }
154
+
155
+ emit(name, detail, options = {}) {
156
+ return this.dispatchEvent(new CustomEvent(name, {
157
+ detail, bubbles: true, composed: true, ...options,
158
+ }));
159
+ }
160
+
161
+ _upgradeProperty(name) {
162
+ if (!Object.prototype.hasOwnProperty.call(this, name)) return;
163
+ const value = this[name];
164
+ delete this[name];
165
+ this[name] = value;
166
+ }
167
+
168
+ _upgradeDeclarativeEvents() {
169
+ for (const element of this.renderRoot.querySelectorAll?.('*') || []) {
170
+ const names = element.getAttributeNames?.() || [...(element.attributes || [])].filter(Boolean).map(attribute => attribute.name);
171
+ for (const attributeName of names) {
172
+ if (!attributeName?.startsWith('@')) continue;
173
+ const [event, ...modifiers] = attributeName.slice(1).split('.');
174
+ element.setAttribute('data-stx-on-' + event, element.getAttribute(attributeName));
175
+ if (modifiers.length) element.setAttribute('data-stx-mod-' + event, modifiers.join(' '));
176
+ element.removeAttribute(attributeName);
177
+ }
178
+ }
179
+ }
180
+
181
+ _bindEvents() {
182
+ for (const event of this.constructor.definition.eventTypes || []) {
183
+ if (this._listeners.has(event)) continue;
184
+ const listener = ($event) => {
185
+ const selector = '[data-stx-on-' + event + ']';
186
+ const target = $event.target?.closest?.(selector);
187
+ if (!target || !this.renderRoot.contains(target)) return;
188
+ const modifiers = (target.getAttribute('data-stx-mod-' + event) || '').split(' ');
189
+ if (modifiers.includes('prevent')) $event.preventDefault();
190
+ if (modifiers.includes('stop')) $event.stopPropagation();
191
+ const method = target.getAttribute('data-stx-on-' + event);
192
+ if (typeof this[method] === 'function') this[method]($event);
193
+ };
194
+ this.renderRoot.addEventListener(event, listener);
195
+ this._listeners.set(event, listener);
196
+ }
197
+ }
198
+
199
+ _render() {
200
+ const definition = this.constructor.definition;
201
+ const root = this.renderRoot;
202
+ const active = root.activeElement || (root.contains?.(globalThis.document?.activeElement) ? globalThis.document.activeElement : null);
203
+ const focusKey = active && (active.getAttribute?.('data-key') || active.id || active.getAttribute?.('name'));
204
+ const selection = active && 'selectionStart' in active ? [active.selectionStart, active.selectionEnd] : null;
205
+ // A compiled component renders itself: the control flow and the derived
206
+ // values were turned into JavaScript at build time, so there is nothing to
207
+ // interpret here and nothing that needs an eval the page's CSP forbids.
208
+ // The string template remains for components that carry no logic.
209
+ let html = typeof this.render === 'function'
210
+ ? this.render(RENDER_HELPERS)
211
+ : definition.template
212
+ .replace(new RegExp("\\{!!\\s*([\\w$.]+)\\s*!!\\}", 'g'), (_match, expression) => String(valueAt(this, expression) ?? ''))
213
+ .replace(new RegExp("\\{\\{\\s*([\\w$.]+)\\s*\\}\\}", 'g'), (_match, expression) => escapeHTML(valueAt(this, expression)));
214
+ if (definition.shadowMode && definition.styles) html = '<style>' + definition.styles + '</style>' + html;
215
+ root.innerHTML = html;
216
+ this._applyBindings();
217
+ this._bindEvents();
218
+ if (focusKey) {
219
+ const escaped = globalThis.CSS?.escape ? globalThis.CSS.escape(focusKey) : focusKey.replace(/["\\]/g, '\$&');
220
+ const next = root.querySelector('[data-key="' + escaped + '"],#' + escaped + ',[name="' + escaped + '"]');
221
+ next?.focus?.();
222
+ if (selection && next?.setSelectionRange) next.setSelectionRange(selection[0], selection[1]);
223
+ }
224
+ this.dispatchEvent?.(new CustomEvent('stx:updated', { bubbles: false }));
225
+ }
226
+
227
+ _applyBindings() {
228
+ for (const binding of this.constructor.definition.bindings || []) {
229
+ const element = this.renderRoot.querySelector('[data-stx-bind-' + binding.id + ']');
230
+ if (!element) continue;
231
+ const value = valueAt(this, binding.expression);
232
+ if (binding.name === 'class') element.className = value || '';
233
+ else if (binding.name === 'style' && typeof value === 'object') {
234
+ Object.assign(element.style, value);
235
+ }
236
+ else if (typeof value === 'boolean') {
237
+ element.toggleAttribute(binding.name, value);
238
+ if (binding.name in element) element[binding.name] = value;
239
+ }
240
+ else if (value == null || value === false) element.removeAttribute(binding.name);
241
+ else element.setAttribute(binding.name, String(value));
242
+ }
243
+ }
244
+ }
245
+
246
+ /**
247
+ * What a compiled render is handed.
248
+ *
249
+ * Passed in rather than closed over so the generated code names no globals, and
250
+ * a component module stays readable on its own: __escape is visibly a
251
+ * parameter, not something the bundler happened to leave in scope.
252
+ */
253
+ const RENDER_HELPERS = {
254
+ escape: escapeHTML,
255
+ // The raw form is the author declaring the value is markup. Null becomes an
256
+ // empty string rather than the word "null", which is the only sensible
257
+ // reading of a missing fragment.
258
+ raw: (value) => String(value ?? ''),
259
+ // A loop over something absent runs zero times. Throwing here would fail a
260
+ // whole page over one empty list, which is never what the author meant.
261
+ values: (value) => value == null ? [] : (value[Symbol.iterator] ? value : Object.values(value)),
262
+ entries: (value) => value == null ? [] : (Array.isArray(value) ? value.entries() : Object.entries(value)),
263
+ };
264
+
265
+ export function defineComponent(Component, definition) {
266
+ const properties = definition.properties || {};
267
+ const attributeToProperty = {};
268
+ const observedAttributes = [];
269
+ for (const [name, property] of Object.entries(properties)) {
270
+ const attribute = property.attribute === false ? null : (property.attribute || name.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase()));
271
+ if (attribute) {
272
+ attributeToProperty[attribute] = name;
273
+ observedAttributes.push(attribute);
274
+ }
275
+ // A style prop is an attribute value, not a replacement for the browser's
276
+ // CSSStyleDeclaration. Native event handler accessors must survive too.
277
+ if (name in HTMLElementBase.prototype) {
278
+ if (!attribute) throw new TypeError('STX prop "' + name + '" overlaps the native DOM API and needs an attribute binding');
279
+ continue;
280
+ }
281
+ Object.defineProperty(Component.prototype, name, {
282
+ configurable: true,
283
+ enumerable: true,
284
+ get() { return this._values[name]; },
285
+ set(value) {
286
+ const oldValue = this._values[name];
287
+ if (Object.is(oldValue, value)) return;
288
+ this._values[name] = value;
289
+ if (property.reflect && attribute && !this._reflecting) {
290
+ const serialized = serialize(value, property.type);
291
+ this._reflecting = true;
292
+ if (serialized === null) this.removeAttribute(attribute);
293
+ else this.setAttribute(attribute, serialized);
294
+ this._reflecting = false;
295
+ }
296
+ this.requestUpdate();
297
+ this.dispatchEvent?.(new CustomEvent(name + '-changed', {
298
+ detail: { value, oldValue }, bubbles: true, composed: true,
299
+ }));
300
+ },
301
+ });
302
+ }
303
+ Component.definition = { ...definition, attributeToProperty, observedAttributes };
304
+ const registry = globalThis.customElements || globalThis.window?.customElements;
305
+ if (registry && !registry.get(definition.tag)) {
306
+ registry.define(definition.tag, Component);
307
+ }
308
+ return Component;
309
+ }
package/dist/ssr.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export declare const renderContextMenu: (props?: ContextMenuProps, slots?: Record<string, string>) => string
2
+ export declare const renderContextMenuItem: (props?: ContextMenuItemProps, slots?: Record<string, string>) => string
3
+ export declare const renderDesktopIcon: (props?: DesktopIconProps, slots?: Record<string, string>) => string
4
+ export declare const renderFeatureButton: (props?: FeatureButtonProps, slots?: Record<string, string>) => string
5
+ export declare const renderInputGroup: (props?: InputGroupProps, slots?: Record<string, string>) => string
6
+ export declare const renderLoginForm: (props?: LoginFormProps, slots?: Record<string, string>) => string
7
+ export declare const renderLoginScreen: (props?: LoginScreenProps, slots?: Record<string, string>) => string
8
+ export declare const renderNotificationPopup: (props?: NotificationPopupProps, slots?: Record<string, string>) => string
9
+ export declare const renderPackageItem: (props?: PackageItemProps, slots?: Record<string, string>) => string
10
+ export declare const renderStartMenu: (props?: StartMenuProps, slots?: Record<string, string>) => string
11
+ export declare const renderStartMenuItem: (props?: StartMenuItemProps, slots?: Record<string, string>) => string
12
+ export declare const renderTaskbar: (props?: TaskbarProps, slots?: Record<string, string>) => string
13
+ export declare const renderWindow: (props?: WindowProps, slots?: Record<string, string>) => string
14
+ export declare function renderComponent(tag: string, props?: Record<string, unknown>, slots?: Record<string, string>): string
15
+ import type { ContextMenuProps } from './stacks-context-menu.js'
16
+ import type { ContextMenuItemProps } from './stacks-context-menu-item.js'
17
+ import type { DesktopIconProps } from './stacks-desktop-icon.js'
18
+ import type { FeatureButtonProps } from './stacks-feature-button.js'
19
+ import type { InputGroupProps } from './stacks-input-group.js'
20
+ import type { LoginFormProps } from './stacks-login-form.js'
21
+ import type { LoginScreenProps } from './stacks-login-screen.js'
22
+ import type { NotificationPopupProps } from './stacks-notification-popup.js'
23
+ import type { PackageItemProps } from './stacks-package-item.js'
24
+ import type { StartMenuProps } from './stacks-start-menu.js'
25
+ import type { StartMenuItemProps } from './stacks-start-menu-item.js'
26
+ import type { TaskbarProps } from './stacks-taskbar.js'
27
+ import type { WindowProps } from './stacks-window.js'