@raylang/elements 0.4.0 → 0.5.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/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. */
package/core/host.js CHANGED
@@ -64,10 +64,11 @@ const INTERACTIVE = "button, a[href], input, select, textarea, label, summary, [
64
64
  /**
65
65
  * Makes `el` drag the window when the page runs under a host's title bar
66
66
  * (titlebar() is not null): it gets data-ray-drag, the attribute the std/ui
67
- * shell reads. The shell looks at the event's target, which a shadow root
68
- * retargets to its host, so for a pointer down on anything interactive
69
- * inside `el` (a button in its shadow root too) the attribute steps aside
70
- * until the pointer is up. Returns the function that undoes it.
67
+ * shell reads. The shell walks the event's composed path, but only spares
68
+ * buttons, links and form fields; for a pointer down on anything else
69
+ * interactive inside `el` (a tab, a summary, a [role=button] in a shadow
70
+ * root) the attribute steps aside until the pointer is up. Returns the
71
+ * function that undoes it.
71
72
  */
72
73
  export function dragRegion(el) {
73
74
  const guard = (e) => {
package/core/raylang.js CHANGED
@@ -29,9 +29,8 @@ const MAC_BUTTONS = 78;
29
29
  * long to wait for that answer; `ray` replaces window.ray (tests).
30
30
  * `windowColors` (default true) keeps the window's background in the
31
31
  * theme's background color, so a resize or a reopen never shows the other
32
- * theme. The title bar is tinted too only when the window has its own bar
33
- * (not full_content) and the theme is light or dark: with "system" it stays
34
- * the system's, so the page keeps following the system.
32
+ * theme. The title bar is tinted too when the theme is light or dark: with
33
+ * "system" it stays the system's, so the page keeps following the system.
35
34
  */
36
35
  export function enableRaylang({ timeout = 2000, ray = globalThis.ray, windowColors = true } = {}) {
37
36
  if (ray !== globalThis.ray) return connect(ray, timeout, windowColors);
@@ -114,22 +113,20 @@ async function connect(ray, timeout, windowColors) {
114
113
  if (windowColors && ops.has("window_colors")) {
115
114
  // A title bar color also sets the window's appearance (macOS picks light
116
115
  // or dark by its luminance), which the page's prefers-color-scheme
117
- // follows, and in a full_content window it brings back the opaque bar.
118
- // So the bar is tinted only in a window with its own bar and a chosen
119
- // theme (light or dark), and given back to the system once after that.
116
+ // follows. So the bar is tinted only for a chosen theme (light or dark),
117
+ // and given back to the system once after that. In a full_content window
118
+ // the bar stays transparent either way.
120
119
  let tinted = false;
121
120
  const paint = (theme) => {
122
121
  const background = hex(theme.background);
123
122
  if (!background) return;
124
123
  const colors = { background };
125
- if (!host.titlebar) {
126
- if (getTheme() !== "system") {
127
- colors.titlebar = background;
128
- tinted = true;
129
- } else if (tinted) {
130
- colors.titlebar = "";
131
- tinted = false;
132
- }
124
+ if (getTheme() !== "system") {
125
+ colors.titlebar = background;
126
+ tinted = true;
127
+ } else if (tinted) {
128
+ colors.titlebar = "";
129
+ tinted = false;
133
130
  }
134
131
  host.setWindowColors(colors).catch(() => {});
135
132
  };
@@ -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.4.0",
3
+ "version": "0.5.0",
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",