@trunkjs/form 1.0.0 → 1.0.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 1.0.4 (2026-09-09)
2
+
3
+ This was a version bump only for form to align it with other projects, there were no code changes.
4
+
5
+ ## 1.0.3 (2026-09-09)
6
+
7
+ This was a version bump only for form to align it with other projects, there were no code changes.
8
+
9
+ ## 1.0.2 (2026-09-07)
10
+
11
+ ### 🧱 Updated Dependencies
12
+
13
+ - Updated vite-demo-viewer to 1.0.12
14
+
15
+ ## 1.0.1 (2026-09-01)
16
+
17
+ ### 🚀 Features
18
+
19
+ - **form:** compose global form presets ([d94865c](https://github.com/trunkjs/trunkjs-monorepo/commit/d94865c))
20
+ - **form:** add ajax form element and remote controls ([0d75fd1](https://github.com/trunkjs/trunkjs-monorepo/commit/0d75fd1))
21
+
22
+ ### 🩹 Fixes
23
+
24
+ - **form:** select a single preset ([cda92d5](https://github.com/trunkjs/trunkjs-monorepo/commit/cda92d5))
25
+
26
+ ### ❤️ Thank You
27
+
28
+ - Matthias Leuffen
29
+
1
30
  # Changelog
2
31
 
3
32
  ## Unreleased
@@ -24,6 +24,8 @@ export declare class TjForm extends HTMLElement {
24
24
  requestSubmit(submitter?: HTMLElement | null, sourceEvent?: Event | null): Promise<unknown>;
25
25
  private readonly handleClick;
26
26
  private isSubmitter;
27
+ private validateControls;
28
+ private isVisibleControl;
27
29
  private createContext;
28
30
  private watchPreset;
29
31
  private activatePreset;
package/index.js CHANGED
@@ -5,12 +5,12 @@ class h {
5
5
  this.presets = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Map();
6
6
  }
7
7
  register(t, e) {
8
- const s = this.normalizeName(t);
9
- return this.presets.set(s, e), this.notify(s, e), this;
8
+ const i = this.normalizeName(t);
9
+ return this.presets.set(i, e), this.notify(i, e), this;
10
10
  }
11
11
  unregister(t) {
12
- const e = t.trim(), s = this.presets.delete(e);
13
- return s && this.notify(e, void 0), s;
12
+ const e = t.trim(), i = this.presets.delete(e);
13
+ return i && this.notify(e, void 0), i;
14
14
  }
15
15
  get(t) {
16
16
  return t ? this.presets.get(t.trim()) : void 0;
@@ -19,9 +19,9 @@ class h {
19
19
  return this.presets.has(t.trim());
20
20
  }
21
21
  subscribe(t, e) {
22
- const s = this.normalizeName(t), i = this.listeners.get(s) ?? /* @__PURE__ */ new Set();
23
- return i.add(e), this.listeners.set(s, i), () => {
24
- i.delete(e), i.size === 0 && this.listeners.delete(s);
22
+ const i = this.normalizeName(t), s = this.listeners.get(i) ?? /* @__PURE__ */ new Set();
23
+ return s.add(e), this.listeners.set(i, s), () => {
24
+ s.delete(e), s.size === 0 && this.listeners.delete(i);
25
25
  };
26
26
  }
27
27
  normalizeName(t) {
@@ -31,12 +31,12 @@ class h {
31
31
  return e;
32
32
  }
33
33
  notify(t, e) {
34
- for (const s of this.listeners.get(t) ?? [])
35
- s(e);
34
+ for (const i of this.listeners.get(t) ?? [])
35
+ i(e);
36
36
  }
37
37
  }
38
38
  const c = globalThis.__trunkjsFormRegistry ?? (globalThis.__trunkjsFormRegistry = new h());
39
- function g(n, t) {
39
+ function b(n, t) {
40
40
  if (typeof n == "string") {
41
41
  if (!t)
42
42
  throw new Error("A named form preset requires a preset definition.");
@@ -47,8 +47,8 @@ function g(n, t) {
47
47
  class d extends HTMLElement {
48
48
  constructor(t = c) {
49
49
  super(), this.registry = t, this.dataAccessor = new l(this), this.unsubscribeRegistry = null, this.pluginCleanups = [], this.handleClick = (e) => {
50
- const s = e.target instanceof Element ? e.target : null, i = s == null ? void 0 : s.closest('button, input, [type="submit"]');
51
- !i || i.closest("tj-form") !== this || !this.isSubmitter(i) || (e.preventDefault(), this.requestSubmit(i, e));
50
+ const i = e.target instanceof Element ? e.target : null, s = i == null ? void 0 : i.closest('button, input, [type="submit"]');
51
+ !s || s.closest("tj-form") !== this || !this.isSubmitter(s) || (e.preventDefault(), this.requestSubmit(s, e));
52
52
  };
53
53
  }
54
54
  static get observedAttributes() {
@@ -91,20 +91,44 @@ class d extends HTMLElement {
91
91
  }
92
92
  async requestSubmit(t = null, e = null) {
93
93
  var r, o;
94
- const s = this.createContext(t, e);
94
+ if (!this.validateControls())
95
+ return;
96
+ const i = this.createContext(t, e);
95
97
  if (this.dispatchEvent(
96
98
  new CustomEvent("tj-form-submit", {
97
99
  bubbles: !0,
98
100
  cancelable: !0,
99
- detail: s
101
+ detail: i
100
102
  })
101
103
  ))
102
- return (o = (r = this.activePreset) == null ? void 0 : r.onSubmit) == null ? void 0 : o.call(r, s);
104
+ return (o = (r = this.activePreset) == null ? void 0 : r.onSubmit) == null ? void 0 : o.call(r, i);
103
105
  }
104
106
  isSubmitter(t) {
105
107
  var e;
106
108
  return t instanceof HTMLButtonElement ? (t.getAttribute("type") ?? "submit").toLowerCase() === "submit" : t instanceof HTMLInputElement ? ["submit", "image"].includes(t.type.toLowerCase()) : ((e = t.getAttribute("type")) == null ? void 0 : e.toLowerCase()) === "submit";
107
109
  }
110
+ // Validates native and form-associated custom controls before dispatching the submit event.
111
+ validateControls() {
112
+ var e, i;
113
+ const t = [];
114
+ for (const s of this.getElements()) {
115
+ const r = s;
116
+ !this.isVisibleControl(r) || typeof r.checkValidity != "function" || r.checkValidity() || t.push(r);
117
+ }
118
+ return t.forEach((s) => {
119
+ var r;
120
+ return (r = s.reportValidity) == null ? void 0 : r.call(s);
121
+ }), (i = (e = t[0]) == null ? void 0 : e.focus) == null || i.call(e), t.length === 0;
122
+ }
123
+ // Uses the browser visibility API where available and falls back to the relevant HTML visibility states.
124
+ isVisibleControl(t) {
125
+ if (t.hidden || t.closest("[hidden]"))
126
+ return !1;
127
+ if (typeof t.checkVisibility == "function")
128
+ return t.checkVisibility({ checkOpacity: !1, checkVisibilityCSS: !0 });
129
+ const e = window.getComputedStyle(t);
130
+ return e.display !== "none" && e.visibility !== "hidden";
131
+ }
108
132
  createContext(t, e) {
109
133
  return {
110
134
  form: this,
@@ -122,8 +146,8 @@ class d extends HTMLElement {
122
146
  if (this.disconnectPlugins(), this.activePreset = t, !!t) {
123
147
  t.value && (this.value = t.value);
124
148
  for (const e of t.plugins ?? []) {
125
- const s = e.connect(this);
126
- s && this.pluginCleanups.push(s);
149
+ const i = e.connect(this);
150
+ i && this.pluginCleanups.push(i);
127
151
  }
128
152
  }
129
153
  }
@@ -132,26 +156,26 @@ class d extends HTMLElement {
132
156
  }
133
157
  }
134
158
  typeof customElements < "u" && !customElements.get("tj-form") && customElements.define("tj-form", d);
135
- class m {
159
+ class f {
136
160
  constructor(t = {}) {
137
161
  this.options = t;
138
162
  }
139
163
  connect(t) {
140
- const e = (s) => {
141
- if (s.key !== "Enter" || s.defaultPrevented || s.isComposing)
164
+ const e = (i) => {
165
+ if (i.key !== "Enter" || i.defaultPrevented || i.isComposing)
142
166
  return;
143
- const i = s.target instanceof HTMLElement ? s.target : null;
144
- if (!i || i.closest("tj-form") !== t || this.isMultiline(i))
167
+ const s = i.target instanceof HTMLElement ? i.target : null;
168
+ if (!s || s.closest("tj-form") !== t || this.isMultiline(s))
145
169
  return;
146
- const r = t.getElements().filter((a) => !this.isDisabled(a)), o = r.find((a) => a === i || a.contains(i));
147
- o && (s.preventDefault(), this.moveNext(t, o, r));
170
+ const r = t.getElements().filter((a) => !this.isDisabled(a)), o = r.find((a) => a === s || a.contains(s));
171
+ o && (i.preventDefault(), this.moveNext(t, o, r));
148
172
  };
149
173
  return t.addEventListener("keydown", e), () => t.removeEventListener("keydown", e);
150
174
  }
151
- async moveNext(t, e, s) {
175
+ async moveNext(t, e, i) {
152
176
  if (!(this.options.validate ? await this.options.validate(e, t) : this.validate(e)))
153
177
  return;
154
- const r = s[s.indexOf(e) + 1];
178
+ const r = i[i.indexOf(e) + 1];
155
179
  r && (this.options.focus ?? ((o) => o.focus()))(r);
156
180
  }
157
181
  validate(t) {
@@ -165,15 +189,15 @@ class m {
165
189
  return t instanceof HTMLTextAreaElement || t.getAttribute("contenteditable") === "true";
166
190
  }
167
191
  }
168
- function b(n = {}) {
169
- return new m(n);
192
+ function g(n = {}) {
193
+ return new f(n);
170
194
  }
171
195
  export {
172
196
  u as DEFAULT_FORM_PRESET,
173
- m as EnterNextPlugin,
197
+ f as EnterNextPlugin,
174
198
  d as TjForm,
175
199
  h as TjFormRegistry,
176
- b as enterNextPlugin,
177
- g as registerFormPreset,
200
+ g as enterNextPlugin,
201
+ b as registerFormPreset,
178
202
  c as tjFormRegistry
179
203
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trunkjs/form",
3
- "version": "1.0.0",
3
+ "version": "1.0.4",
4
4
  "main": "./index.js",
5
5
  "repository": {
6
6
  "directory": "packages/form",