@xoong/core 0.0.39 → 0.0.42

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/package.json CHANGED
@@ -1,14 +1,26 @@
1
1
  {
2
2
  "name": "@xoong/core",
3
- "version": "0.0.39",
3
+ "version": "0.0.42",
4
4
  "description": "xoong core: signals, Light DOM custom elements, html templates. Zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
- ".": "./src/index.js",
9
- "./signals": "./src/signals.js",
10
- "./closest": "./src/closest.js",
11
- "./syntax": "./src/syntax.js"
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "default": "./src/index.js"
11
+ },
12
+ "./signals": {
13
+ "types": "./src/signals.d.ts",
14
+ "default": "./src/signals.js"
15
+ },
16
+ "./closest": {
17
+ "types": "./src/closest.d.ts",
18
+ "default": "./src/closest.js"
19
+ },
20
+ "./syntax": {
21
+ "types": "./src/syntax.d.ts",
22
+ "default": "./src/syntax.js"
23
+ }
12
24
  },
13
25
  "sideEffects": false,
14
26
  "license": "MIT",
@@ -30,7 +42,7 @@
30
42
  "bugs": {
31
43
  "url": "https://github.com/fpt-duongtx/xoong/issues"
32
44
  },
33
- "homepage": "https://github.com/fpt-duongtx/xoong#readme",
45
+ "homepage": "https://fpt-duongtx.github.io/xoong/",
34
46
  "publishConfig": {
35
47
  "access": "public"
36
48
  },
@@ -0,0 +1,7 @@
1
+ // Types for @xoong/core/closest: "did you mean" helpers.
2
+
3
+ /** Edit distance (Levenshtein) between two strings. */
4
+ export function distance(a: string, b: string): number;
5
+
6
+ /** The candidate closest to `ref` (case-insensitive), or undefined when nothing is close enough. */
7
+ export function nearest(ref: string, candidates: Iterable<string>): string | undefined;
@@ -0,0 +1,101 @@
1
+ // Types for XoControl, the base class of form-associated custom elements.
2
+ import { XoElement } from './element.js';
3
+
4
+ /** The validity state of a control: the same names as the native `ValidityState`, as a plain object. */
5
+ export interface XoValidity {
6
+ readonly valid: boolean;
7
+ readonly valueMissing: boolean;
8
+ readonly customError: boolean;
9
+ readonly badInput: boolean;
10
+ readonly typeMismatch: boolean;
11
+ readonly patternMismatch: boolean;
12
+ readonly rangeOverflow: boolean;
13
+ readonly rangeUnderflow: boolean;
14
+ readonly stepMismatch: boolean;
15
+ readonly tooLong: boolean;
16
+ readonly tooShort: boolean;
17
+ }
18
+
19
+ /**
20
+ * Base class for custom form controls. It behaves like a native input in a `<form>`: it submits `name` and `value` through ElementInternals,
21
+ * takes part in constraint validation, resets with the form, is disabled with its fieldset, and tracks `dirty`, `touched` and `pristine`.
22
+ * Subclass it, render the inner UI, and call `userInput(value)` when the person changes the value.
23
+ *
24
+ * class StarRating extends XoControl {
25
+ * static props = { ...XoControl.props, max: Number };
26
+ * declare max: number;
27
+ * render() { return html`<button @click=${() => this.userInput(Number(this.value) + 1)}>+</button>`; }
28
+ * validate(value: unknown) { return Number(value) > this.max ? 'Too many stars' : ''; }
29
+ * }
30
+ * define('star-rating', StarRating);
31
+ */
32
+ export class XoControl extends XoElement {
33
+ static formAssociated: boolean;
34
+ static props: { name: StringConstructor; required: BooleanConstructor; disabled: BooleanConstructor; readonly: BooleanConstructor };
35
+
36
+ constructor();
37
+
38
+ /** The form field name. */
39
+ name: string;
40
+ required: boolean;
41
+ disabled: boolean;
42
+ readonly: boolean;
43
+
44
+ // ---- value
45
+ /** The value. A string by default; a subclass may hold anything and override `serialize`. Reading it is tracked. */
46
+ get value(): any;
47
+ set value(v: any);
48
+ /** Set the value programmatically (like `input.value = x`: no events). Before the control has started, it also becomes the initial value. */
49
+ setValue(v: any): void;
50
+ /** The person changed the value: sets it and fires `input`. */
51
+ userInput(v: any): void;
52
+ /** The person finished a change: fires `change`. */
53
+ userChange(): void;
54
+ /** What the form submits for a value: a string, a File, FormData, or null for nothing. Override for structured values. */
55
+ serialize(v: any): string | File | FormData | null;
56
+ /** Whether a value counts as "nothing entered" for `required`. Override for structured values. */
57
+ isEmpty(v: any): boolean;
58
+ /** Validation hook: return an error message for an invalid value, a falsy value when it is valid. Runs on every change. */
59
+ validate(value: any): string | null | undefined | false;
60
+
61
+ // ---- state (reading these inside a template or an effect is tracked)
62
+ /** The value the control started with (what `reset` goes back to and `dirty` compares with). */
63
+ get initialValue(): any;
64
+ get dirty(): boolean;
65
+ get pristine(): boolean;
66
+ get touched(): boolean;
67
+ /** True when there is no validation error. */
68
+ get valid(): boolean;
69
+ get invalid(): boolean;
70
+ /** Mark the control as visited (blur does this). */
71
+ markTouched(): void;
72
+
73
+ // ---- validation
74
+ /** The message to show, or '' when valid. */
75
+ get validationMessage(): string;
76
+ /** Disabled by its own attribute or by a disabled fieldset. */
77
+ get isDisabled(): boolean;
78
+ get willValidate(): boolean;
79
+ get validity(): XoValidity;
80
+ /** Set your own error (cross-field rules, server errors). An empty string clears it. */
81
+ setCustomValidity(message: string | null | undefined): void;
82
+ /** True when valid (or not validated); otherwise fires a cancelable `invalid` event and returns false. */
83
+ checkValidity(): boolean;
84
+ /** Like `checkValidity`, and on failure marks the control touched and focuses `focusTarget`. */
85
+ reportValidity(): boolean;
86
+ /** The element inside that takes focus (and anchors the validation bubble). */
87
+ get focusTarget(): HTMLElement | null;
88
+
89
+ // ---- reset
90
+ /** Back to the initial value, untouched. The form calls this on reset. */
91
+ reset(): void;
92
+ /** Make the current value the new baseline, as after a successful save: `dirty` becomes false. */
93
+ markPristine(): void;
94
+
95
+ // ---- form-associated callbacks
96
+ formResetCallback(): void;
97
+ formDisabledCallback(disabled: boolean): void;
98
+ formStateRestoreCallback(state: any): void;
99
+ /** The form this control belongs to (through ElementInternals, else the closest `<form>`). */
100
+ get form(): HTMLFormElement | null;
101
+ }
@@ -0,0 +1,57 @@
1
+ // Types for XoElement and define.
2
+ import type { Signal } from './signals.js';
3
+ import type { TemplateResult } from './html.js';
4
+
5
+ /** The attribute types a prop can have; the attribute is converted to it (Boolean: present and not "false"). */
6
+ export type XoPropType = StringConstructor | NumberConstructor | BooleanConstructor;
7
+
8
+ /**
9
+ * Light DOM base element.
10
+ *
11
+ * `static props` declares attributes that are also properties and signals; `define` installs the accessors, so TypeScript needs to be told about them.
12
+ * Declare each one in the subclass with `declare` (it emits nothing):
13
+ *
14
+ * class UserCard extends XoElement {
15
+ * static props = { name: String, age: Number };
16
+ * declare name: string;
17
+ * declare age: number;
18
+ * render() { return html`<h3>${() => this.name}</h3>`; }
19
+ * }
20
+ * define('user-card', UserCard);
21
+ */
22
+ export class XoElement extends HTMLElement {
23
+ /** Attributes <-> signals. The attribute is the kebab-case of the name (`maxSize` is `max-size`). */
24
+ static props: Record<string, XoPropType>;
25
+ static get observedAttributes(): string[];
26
+
27
+ constructor();
28
+
29
+ /** The signal behind a prop (reading `this.name` already tracks it). */
30
+ _sig(name: string): Signal<any>;
31
+
32
+ attributeChangedCallback(attr: string, oldValue: string | null, value: string | null): void;
33
+ connectedCallback(): void;
34
+ disconnectedCallback(): void;
35
+
36
+ /**
37
+ * Implement it only if the element OWNS its content: the result is rendered into the element when it connects.
38
+ * To enhance server-rendered HTML in place, implement `connected()` instead.
39
+ */
40
+ render?(): TemplateResult | null | undefined | void;
41
+ /** Runs after `render()` when the element is connected. */
42
+ connected?(): void;
43
+ /** Runs after the element's effects and rendering were disposed, when it is disconnected. */
44
+ disconnected?(): void;
45
+
46
+ /** Resolves with the element when its children are there: at once once the document is parsed, else after the parser reached the end. Await it in `connected()`. */
47
+ ready(): Promise<this>;
48
+
49
+ /** A side effect bound to this element's lifetime: disposed on disconnect. */
50
+ effect(fn: () => void | (() => void)): void;
51
+
52
+ /** Dispatch a bubbling, composed CustomEvent. */
53
+ emit<T = unknown>(name: string, detail?: T): void;
54
+ }
55
+
56
+ /** Register a custom element (idempotent: a tag that is already defined is left alone). The tag must contain a hyphen. */
57
+ export function define(tag: string, cls: typeof XoElement): void;
package/src/html.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ // Types for the `html` template and `render` of @xoong/core.
2
+
3
+ /** What `html` returns: the strings and values of one template call. Nothing touches the DOM until `render` (or an element's `render()`) uses it. */
4
+ export class TemplateResult {
5
+ constructor(strings: TemplateStringsArray, values: unknown[]);
6
+ readonly strings: TemplateStringsArray;
7
+ readonly values: unknown[];
8
+ }
9
+
10
+ /**
11
+ * Tagged template with fine-grained reactive bindings (Light DOM, no virtual DOM). A function in a child or attribute position is reactive.
12
+ *
13
+ * html`<h3 class=${() => cls.value}>${() => name.value}</h3><button @click=${() => save()}>Save</button>`
14
+ *
15
+ * `name=${v}` sets an attribute (null/false removes it), `?name=${v}` a boolean attribute, `.name=${v}` a DOM property, `@event=${fn}` a listener,
16
+ * `style:--x=${v}` a CSS custom property through the CSS object model.
17
+ */
18
+ export function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
19
+
20
+ /** Render a template result into `container`, replacing its children. Returns the dispose function. */
21
+ export function render(result: TemplateResult, container: Element | DocumentFragment): () => void;
package/src/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Types for @xoong/core. The runtime is plain JavaScript; these files describe it (one per source module).
2
+ export { signal, computed, effect, batch, untracked, disposeComputed, Signal } from './signals.js';
3
+ export type { ReadonlySignal } from './signals.js';
4
+ export { html, render, TemplateResult } from './html.js';
5
+ export { XoElement, define } from './element.js';
6
+ export type { XoPropType } from './element.js';
7
+ export { XoControl } from './control.js';
8
+ export type { XoValidity } from './control.js';
9
+ export { text, setTextProvider } from './text.js';
10
+ export type { TextProvider } from './text.js';
@@ -0,0 +1,39 @@
1
+ // Types for @xoong/core/signals. The runtime is plain JavaScript; this file describes it.
2
+
3
+ /** A readable reactive value. Reading `value` inside a computed, an effect or a template function is tracked. */
4
+ export interface ReadonlySignal<T> {
5
+ readonly value: T;
6
+ /** Read without tracking. */
7
+ peek(): T;
8
+ }
9
+
10
+ /** A writable reactive value. Writing a different value (by `Object.is`) runs the effects that read it. */
11
+ export class Signal<T> implements ReadonlySignal<T> {
12
+ constructor(value: T);
13
+ get value(): T;
14
+ set value(next: T);
15
+ /** Read without tracking. */
16
+ peek(): T;
17
+ /** Set the value from the previous one. */
18
+ update(fn: (previous: T) => T): void;
19
+ }
20
+
21
+ export function signal<T>(value: T): Signal<T>;
22
+
23
+ /** A value derived from other signals; it recomputes lazily, when something it read has changed. */
24
+ export function computed<T>(fn: () => T): ReadonlySignal<T>;
25
+
26
+ /** Let a computed release its subscriptions when its owner is discarded. */
27
+ export function disposeComputed(c: { detach?: () => void }): void;
28
+
29
+ /**
30
+ * Run `fn` now and again whenever a signal it read changes. `fn` may return a cleanup callback, which runs before the next run and on dispose.
31
+ * Returns the dispose function. For side effects only: never fetch data in an effect, declare the operation on a view instead.
32
+ */
33
+ export function effect(fn: () => void | (() => void)): () => void;
34
+
35
+ /** Group writes: effects run once, after the outermost batch ends. Returns what `fn` returns. */
36
+ export function batch<T>(fn: () => T): T;
37
+
38
+ /** Read signals inside `fn` without subscribing to them. */
39
+ export function untracked<T>(fn: () => T): T;
@@ -0,0 +1,14 @@
1
+ // Types for @xoong/core/syntax: the names of xoong's declarative HTML syntax (what `xoong check` validates against).
2
+
3
+ /** Attributes understood inside `<xo-view>`. */
4
+ export const XO_ATTRIBUTES: string[];
5
+ /** Custom elements shipped by xoong. */
6
+ export const XO_ELEMENTS: string[];
7
+ /** Attributes that come with @xoong/ui (the admin set). */
8
+ export const XO_UI_ATTRIBUTES: string[];
9
+ /** Elements that come with @xoong/ui. */
10
+ export const XO_UI_ELEMENTS: string[];
11
+ /** Attributes of @xoong/controls and @xoong/richtext. */
12
+ export const XO_CONTROL_ATTRIBUTES: string[];
13
+ /** Elements of @xoong/controls and @xoong/richtext. */
14
+ export const XO_CONTROL_ELEMENTS: string[];
package/src/syntax.js CHANGED
@@ -15,5 +15,5 @@ export const XO_UI_ATTRIBUTES = ['xo-open', 'xo-close', 'xo-route', 'xo-layout',
15
15
  export const XO_UI_ELEMENTS = ['xo-table', 'xo-form', 'xo-dialog', 'xo-toast', 'xo-router'];
16
16
 
17
17
  /** Attributes and elements of @xoong/controls and @xoong/richtext. */
18
- export const XO_CONTROL_ATTRIBUTES = ['xo-tab', 'xo-menu-trigger', 'xo-tooltip'];
19
- export const XO_CONTROL_ELEMENTS = ['xo-tabs', 'xo-menu', 'xo-combobox', 'xo-tags', 'xo-richtext', 'xo-carousel', 'xo-switch', 'xo-date', 'xo-time', 'xo-slider', 'xo-stepper', 'xo-password', 'xo-copy', 'xo-image'];
18
+ export const XO_CONTROL_ATTRIBUTES = ['xo-tab', 'xo-menu-trigger', 'xo-tooltip', 'xo-consent-open'];
19
+ export const XO_CONTROL_ELEMENTS = ['xo-tabs', 'xo-menu', 'xo-combobox', 'xo-tags', 'xo-richtext', 'xo-carousel', 'xo-switch', 'xo-date', 'xo-time', 'xo-slider', 'xo-stepper', 'xo-password', 'xo-copy', 'xo-image', 'xo-theme-toggle', 'xo-accordion', 'xo-popover', 'xo-consent', 'xo-file', 'xo-command', 'xo-chart', 'xo-range-slider', 'xo-segmented', 'xo-split-button', 'xo-textarea', 'xo-stat', 'xo-otp', 'xo-avatar', 'xo-avatars', 'xo-progress', 'xo-spinner', 'xo-steps', 'xo-inline-edit'];
package/src/text.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ // Types for the text hook of @xoong/core.
2
+
3
+ /** A provider answers with the translated message for a key, or undefined when it has none. `@xoong/i18n` installs one. */
4
+ export type TextProvider = (key: string, args?: Record<string, any>) => string | undefined;
5
+
6
+ /** Install (or remove, with null) the provider that translates the framework's own texts. */
7
+ export function setTextProvider(fn: TextProvider | null): void;
8
+
9
+ /**
10
+ * The text for a stable key such as "xoong.table.next": the provider's translation, else the English `fallback`
11
+ * (`{name}` placeholders and the simple `{n, plural, one {# thing} other {# things}}` form are filled from `args`).
12
+ * Applications use `t()` from @xoong/i18n instead.
13
+ */
14
+ export function text(key: string, fallback: string, args?: Record<string, any>): string;