@raylang/elements 0.3.0 → 0.4.1

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/base.css CHANGED
@@ -108,4 +108,9 @@
108
108
  :where(.ray-table-bordered) :where(tbody tr:last-child td) {
109
109
  border-bottom: 0;
110
110
  }
111
+
112
+ /* Secondary text (hints, metadata): <span class="ray-muted">. */
113
+ :where(.ray-muted) {
114
+ color: var(--ray-muted-foreground);
115
+ }
111
116
  }
@@ -14,6 +14,8 @@ const styles = css`
14
14
  const PASSTHROUGH = ["placeholder", "autocomplete", "inputmode", "pattern", "min", "max", "step", "minlength", "maxlength", "spellcheck", "autocapitalize", "enterkeyhint", "list"];
15
15
 
16
16
  export class RayInput extends FieldControl {
17
+ /** Enter submits the form (core/form.js). */
18
+ static implicitSubmission = true;
17
19
  static properties = {
18
20
  ...FieldControl.fieldProperties,
19
21
  type: { type: String, reflect: true, default: "text" },
@@ -50,6 +52,7 @@ export class RayInput extends FieldControl {
50
52
  this._commit();
51
53
  });
52
54
  this.control.addEventListener("change", (e) => this.forward(e));
55
+ this.submitOnEnter(this.control);
53
56
  if (this.autofocus) queueMicrotask(() => this.control.focus());
54
57
  }
55
58
 
@@ -61,6 +61,8 @@ export function formatNumber(n, { decimals, thousands = "", decimal = "." } = {}
61
61
  }
62
62
 
63
63
  export class RayNumberInput extends FieldControl {
64
+ /** Enter submits the form (core/form.js). */
65
+ static implicitSubmission = true;
64
66
  static properties = {
65
67
  ...FieldControl.fieldProperties,
66
68
  value: { type: Number },
@@ -104,6 +106,7 @@ export class RayNumberInput extends FieldControl {
104
106
  });
105
107
  c.addEventListener("change", (e) => this.forward(e));
106
108
  c.addEventListener("keydown", (e) => this._key(e));
109
+ this.submitOnEnter(c);
107
110
  // + and −: a press steps once; held, it repeats faster and faster.
108
111
  for (const [sel, dir] of [[".up", 1], [".down", -1]]) {
109
112
  const b = this.$(sel);
package/core/form.js CHANGED
@@ -44,6 +44,52 @@ export class FormControl extends RayElement {
44
44
  forward(event) {
45
45
  this.dispatchEvent(new Event(event.type, { bubbles: true, cancelable: event.cancelable }));
46
46
  }
47
+
48
+ /**
49
+ * Enter in `control` submits the form, as in a native text field. The
50
+ * native control in a shadow root has no form owner, so the browser's own
51
+ * implicit submission never reaches the <form>: this does it the way HTML
52
+ * does (see implicitSubmit).
53
+ */
54
+ submitOnEnter(control) {
55
+ control.addEventListener("keydown", (e) => {
56
+ if (e.key !== "Enter" || e.isComposing || e.defaultPrevented) return;
57
+ const form = this.form;
58
+ if (!form) return;
59
+ e.preventDefault();
60
+ implicitSubmit(form);
61
+ });
62
+ }
63
+ }
64
+
65
+ // Native controls whose Enter submits (HTML's "fields that block implicit
66
+ // submission").
67
+ const TEXT_TYPES = ["text", "search", "url", "tel", "email", "password", "date", "month", "week", "time", "datetime-local", "number"];
68
+
69
+ // A native submit button, or a form-associated custom element with
70
+ // type="submit" (ray-button, or an app's own).
71
+ const isSubmitButton = (el) =>
72
+ (el.localName === "button" && (el.getAttribute("type") ?? "submit") === "submit") ||
73
+ (el.localName === "input" && ["submit", "image"].includes(el.type)) ||
74
+ (el.constructor.formAssociated === true && el.type === "submit");
75
+
76
+ const blocksImplicitSubmission = (el) =>
77
+ el.constructor.implicitSubmission === true || (el.localName === "input" && TEXT_TYPES.includes(el.type));
78
+
79
+ /**
80
+ * HTML's implicit submission of `form`: a click on its first submit button
81
+ * (nothing when that button is disabled), or, without one, a submit when the
82
+ * form has a single text field.
83
+ * @param {HTMLFormElement} form
84
+ */
85
+ export function implicitSubmit(form) {
86
+ const elements = [...form.elements];
87
+ const button = elements.find(isSubmitButton);
88
+ if (button) {
89
+ if (!button.matches(":disabled")) button.click();
90
+ return;
91
+ }
92
+ if (elements.filter(blocksImplicitSubmission).length <= 1) form.requestSubmit();
47
93
  }
48
94
 
49
95
  /** The field wrapper around a control: label, description, box, error. */
@@ -4116,7 +4116,7 @@
4116
4116
  "name": "RayInput",
4117
4117
  "tagName": "ray-input",
4118
4118
  "customElement": true,
4119
- "description": "Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input.",
4119
+ "description": "Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input. Enter submits its form as a native field does: through the first submit button (ray-button type=\"submit\" included; nothing when it is disabled), or, without one, only when the form has a single text field.",
4120
4120
  "superclass": {
4121
4121
  "name": "FieldControl"
4122
4122
  },
@@ -4544,7 +4544,7 @@
4544
4544
  "name": "RayNumberInput",
4545
4545
  "tagName": "ray-number-input",
4546
4546
  "customElement": true,
4547
- "description": "Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow).",
4547
+ "description": "Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow). Enter submits its form, as in ray-input.",
4548
4548
  "superclass": {
4549
4549
  "name": "FieldControl"
4550
4550
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raylang/elements",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Web components for any app, with raylang extras (shadcn/ui + Mantine inspired)",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": ["web-components", "custom-elements", "design-system", "ui", "raylang"],
package/ray-elements.d.ts CHANGED
@@ -804,7 +804,7 @@ export interface RayFileUploadEventMap extends HTMLElementEventMap {
804
804
  }
805
805
 
806
806
  // --- Input (input)
807
- /** <ray-input> Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input. */
807
+ /** <ray-input> Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input. Enter submits its form as a native field does: through the first submit button (ray-button type="submit" included; nothing when it is disabled), or, without one, only when the form has a single text field. */
808
808
  export declare class RayInput extends FieldControl {
809
809
  /** Current value. The value attribute is the initial value (restored on form reset). */
810
810
  value: string | undefined;
@@ -843,7 +843,7 @@ export declare class RayInput extends FieldControl {
843
843
  export declare function parseNumber(text: string, options?: { decimal?: string; thousands?: string }): number;
844
844
  /** A number as text with fixed decimals and separators. */
845
845
  export declare function formatNumber(n: number, options?: { decimals?: number; thousands?: string; decimal?: string }): string;
846
- /** <ray-number-input> Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow). */
846
+ /** <ray-number-input> Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow). Enter submits its form, as in ray-input. */
847
847
  export declare class RayNumberInput extends FieldControl {
848
848
  /** The number (undefined when empty). */
849
849
  value: number | undefined;
@@ -1266,7 +1266,7 @@
1266
1266
  },
1267
1267
  {
1268
1268
  "name": "ray-input",
1269
- "description": "Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input.",
1269
+ "description": "Also forwards placeholder, autocomplete, inputmode, pattern, min, max, step, minlength, maxlength, spellcheck, autocapitalize, enterkeyhint and list to the inner input. Enter submits its form as a native field does: through the first submit button (ray-button type=\"submit\" included; nothing when it is disabled), or, without one, only when the form has a single text field.",
1270
1270
  "attributes": [
1271
1271
  {
1272
1272
  "name": "value",
@@ -1454,7 +1454,7 @@
1454
1454
  },
1455
1455
  {
1456
1456
  "name": "ray-number-input",
1457
- "description": "Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow).",
1457
+ "description": "Arrow Up/Down step (Shift: ×10), Page Up/Down ×10, Home/End go to min/max. The value is a number (undefined when empty); the form gets its plain digits; min and max are checked (rangeUnderflow, rangeOverflow). Enter submits its form, as in ray-input.",
1458
1458
  "attributes": [
1459
1459
  {
1460
1460
  "name": "value",