@tachui/primitives 0.8.0-alpha

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 (49) hide show
  1. package/LICENSE +363 -0
  2. package/README.md +458 -0
  3. package/dist/BasicInput-CWFHw9rL.js +390 -0
  4. package/dist/Divider-DWgDKavU.js +474 -0
  5. package/dist/HTML-aWcvQ0VC.js +321 -0
  6. package/dist/Picker-DrZ7ibdt.js +1675 -0
  7. package/dist/Text-DYyFBJ6i.js +165 -0
  8. package/dist/controls/Button.d.ts +250 -0
  9. package/dist/controls/Button.d.ts.map +1 -0
  10. package/dist/controls/Link.d.ts +365 -0
  11. package/dist/controls/Link.d.ts.map +1 -0
  12. package/dist/controls/Picker.d.ts +153 -0
  13. package/dist/controls/Picker.d.ts.map +1 -0
  14. package/dist/controls/Toggle.d.ts +207 -0
  15. package/dist/controls/Toggle.d.ts.map +1 -0
  16. package/dist/controls/index.d.ts +8 -0
  17. package/dist/controls/index.d.ts.map +1 -0
  18. package/dist/controls/index.js +18 -0
  19. package/dist/display/HTML.d.ts +120 -0
  20. package/dist/display/HTML.d.ts.map +1 -0
  21. package/dist/display/Image.d.ts +192 -0
  22. package/dist/display/Image.d.ts.map +1 -0
  23. package/dist/display/Text.d.ts +269 -0
  24. package/dist/display/Text.d.ts.map +1 -0
  25. package/dist/display/index.d.ts +7 -0
  26. package/dist/display/index.d.ts.map +1 -0
  27. package/dist/display/index.js +21 -0
  28. package/dist/forms/BasicForm.d.ts +141 -0
  29. package/dist/forms/BasicForm.d.ts.map +1 -0
  30. package/dist/forms/BasicInput.d.ts +116 -0
  31. package/dist/forms/BasicInput.d.ts.map +1 -0
  32. package/dist/forms/index.d.ts +6 -0
  33. package/dist/forms/index.d.ts.map +1 -0
  34. package/dist/forms/index.js +10 -0
  35. package/dist/index.d.ts +11 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +485 -0
  38. package/dist/layout/Divider.d.ts +168 -0
  39. package/dist/layout/Divider.d.ts.map +1 -0
  40. package/dist/layout/Spacer.d.ts +35 -0
  41. package/dist/layout/Spacer.d.ts.map +1 -0
  42. package/dist/layout/Stack.d.ts +74 -0
  43. package/dist/layout/Stack.d.ts.map +1 -0
  44. package/dist/layout/index.d.ts +7 -0
  45. package/dist/layout/index.d.ts.map +1 -0
  46. package/dist/layout/index.js +15 -0
  47. package/dist/validation.d.ts +91 -0
  48. package/dist/validation.d.ts.map +1 -0
  49. package/package.json +82 -0
@@ -0,0 +1,390 @@
1
+ import { withModifiers as y, createSignal as b, createEffect as u, h as p, text as g, ComponentWithCSSClasses as E, isSignal as h } from "@tachui/core";
2
+ var v = Object.defineProperty, S = (t, e, s) => e in t ? v(t, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : t[e] = s, o = (t, e, s) => S(t, typeof e != "symbol" ? e + "" : e, s);
3
+ class F {
4
+ constructor(e) {
5
+ this.props = e, o(this, "type", "component"), o(this, "id"), o(this, "mounted", !1), o(this, "cleanup", []), o(this, "formElement", null), o(this, "validationErrors"), o(this, "setValidationErrors"), o(this, "setIsValid"), o(this, "handleSubmit", async (n) => {
6
+ if (n.preventDefault(), this.props.validateOnSubmit !== !1) {
7
+ const a = this.validateForm();
8
+ if (this.setValidationErrors(a), a.length > 0)
9
+ return;
10
+ }
11
+ if (this.props.onSubmit) {
12
+ const a = this.extractFormData();
13
+ await this.props.onSubmit(a);
14
+ }
15
+ }), o(this, "handleChange", () => {
16
+ if (this.props.validateOnChange) {
17
+ const n = this.validateForm();
18
+ this.setValidationErrors(n);
19
+ }
20
+ }), this.id = `form-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
21
+ const [s, r] = b([]), [, i] = b(!0);
22
+ this.validationErrors = s, this.setValidationErrors = r, this.setIsValid = i, u(() => {
23
+ const n = this.validationErrors(), a = n.length === 0;
24
+ this.setIsValid(a), this.props.onValidationChange && this.props.onValidationChange(a, n);
25
+ });
26
+ }
27
+ /**
28
+ * Extract form data from form elements
29
+ */
30
+ extractFormData() {
31
+ if (!this.formElement) return {};
32
+ const e = new FormData(this.formElement), s = {};
33
+ for (const [r, i] of e.entries())
34
+ s[r] ? Array.isArray(s[r]) ? s[r].push(i) : s[r] = [s[r], i] : s[r] = i;
35
+ return s;
36
+ }
37
+ /**
38
+ * Validate all form fields
39
+ */
40
+ validateForm() {
41
+ if (!this.formElement) return [];
42
+ const e = [];
43
+ return this.formElement.querySelectorAll("input, select, textarea").forEach((r) => {
44
+ const i = r, n = i.name || i.id;
45
+ i.checkValidity() || e.push({
46
+ field: n,
47
+ message: i.validationMessage,
48
+ code: "html5_validation"
49
+ }), i.hasAttribute("required") && !i.value.trim() && e.push({
50
+ field: n,
51
+ message: `${n} is required`,
52
+ code: "required"
53
+ });
54
+ }), e;
55
+ }
56
+ /**
57
+ * Get form styling based on style prop
58
+ */
59
+ getFormStyles() {
60
+ const { style: e = "automatic", spacing: s = 16 } = this.props, r = {
61
+ display: "flex",
62
+ flexDirection: "column",
63
+ gap: `${s}px`
64
+ };
65
+ switch (e) {
66
+ case "grouped":
67
+ return {
68
+ ...r,
69
+ backgroundColor: "#ffffff",
70
+ border: "1px solid #e0e0e0",
71
+ borderRadius: "12px",
72
+ padding: "20px",
73
+ boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
74
+ };
75
+ case "inset":
76
+ return {
77
+ ...r,
78
+ backgroundColor: "#f8f9fa",
79
+ border: "1px solid #e9ecef",
80
+ borderRadius: "8px",
81
+ padding: "16px",
82
+ margin: "0 16px"
83
+ };
84
+ case "plain":
85
+ return r;
86
+ default:
87
+ return {
88
+ ...r,
89
+ backgroundColor: "#ffffff",
90
+ borderRadius: "8px",
91
+ padding: "16px"
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Render validation summary
97
+ */
98
+ renderValidationSummary() {
99
+ const e = this.validationErrors();
100
+ return !this.props.showValidationSummary || e.length === 0 ? null : p(
101
+ "div",
102
+ {
103
+ style: {
104
+ backgroundColor: "#fff5f5",
105
+ border: "1px solid #fed7d7",
106
+ borderRadius: "6px",
107
+ padding: "12px 16px",
108
+ marginBottom: "16px"
109
+ }
110
+ },
111
+ p(
112
+ "div",
113
+ {
114
+ style: {
115
+ fontSize: "14px",
116
+ fontWeight: "600",
117
+ color: "#e53e3e",
118
+ marginBottom: "8px"
119
+ }
120
+ },
121
+ g("Please fix the following errors:")
122
+ ),
123
+ p(
124
+ "ul",
125
+ {
126
+ style: {
127
+ margin: "0",
128
+ paddingLeft: "20px",
129
+ fontSize: "14px",
130
+ color: "#c53030"
131
+ }
132
+ },
133
+ ...e.map(
134
+ (s) => p("li", { key: s.field }, g(s.message))
135
+ )
136
+ )
137
+ );
138
+ }
139
+ render() {
140
+ const {
141
+ children: e = [],
142
+ accessibilityLabel: s,
143
+ accessibilityRole: r = "form"
144
+ } = this.props;
145
+ return [
146
+ p(
147
+ "form",
148
+ {
149
+ ref: (i) => {
150
+ this.formElement = i, i && !this.mounted && (i.addEventListener("submit", this.handleSubmit), i.addEventListener("change", this.handleChange), i.addEventListener("input", this.handleChange), this.cleanup.push(() => {
151
+ i.removeEventListener("submit", this.handleSubmit), i.removeEventListener("change", this.handleChange), i.removeEventListener("input", this.handleChange);
152
+ }), this.mounted = !0);
153
+ },
154
+ style: this.getFormStyles(),
155
+ "aria-label": s,
156
+ role: r,
157
+ noValidate: !0
158
+ // We handle validation manually
159
+ },
160
+ ...this.renderValidationSummary() !== null ? [this.renderValidationSummary()] : [],
161
+ ...e.flatMap((i) => i.render())
162
+ )
163
+ ];
164
+ }
165
+ }
166
+ function d(t, e = {}) {
167
+ const s = { ...e, children: t }, r = new F(s);
168
+ return y(r);
169
+ }
170
+ const B = {
171
+ /**
172
+ * Automatic form styling (default)
173
+ */
174
+ Automatic(t, e = {}) {
175
+ return d(t, { ...e, style: "automatic" });
176
+ },
177
+ /**
178
+ * Grouped form with container styling
179
+ */
180
+ Grouped(t, e = {}) {
181
+ return d(t, { ...e, style: "grouped" });
182
+ },
183
+ /**
184
+ * Inset form styling
185
+ */
186
+ Inset(t, e = {}) {
187
+ return d(t, { ...e, style: "inset" });
188
+ },
189
+ /**
190
+ * Plain form without styling
191
+ */
192
+ Plain(t, e = {}) {
193
+ return d(t, { ...e, style: "plain" });
194
+ }
195
+ }, I = {
196
+ /**
197
+ * Common validation rules
198
+ */
199
+ rules: {
200
+ // biome-ignore lint/suspicious/noExplicitAny: Validation rules accept any input type
201
+ required: (t) => t != null && t !== "",
202
+ email: (t) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t),
203
+ minLength: (t) => (e) => e.length >= t,
204
+ maxLength: (t) => (e) => e.length <= t,
205
+ pattern: (t) => (e) => t.test(e),
206
+ number: (t) => !Number.isNaN(Number(t)),
207
+ integer: (t) => Number.isInteger(Number(t)),
208
+ min: (t) => (e) => Number(e) >= t,
209
+ max: (t) => (e) => Number(e) <= t
210
+ },
211
+ /**
212
+ * Validate a single field
213
+ */
214
+ validateField(t, e) {
215
+ for (const s of e) {
216
+ const r = s(t);
217
+ if (r !== !0)
218
+ return {
219
+ field: "field",
220
+ message: typeof r == "string" ? r : "Validation failed",
221
+ code: "custom_validation"
222
+ };
223
+ }
224
+ return null;
225
+ }
226
+ };
227
+ var V = Object.defineProperty, T = (t, e, s) => e in t ? V(t, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : t[e] = s, l = (t, e, s) => T(t, typeof e != "symbol" ? e + "" : e, s);
228
+ const c = {
229
+ colors: {
230
+ background: "#FFFFFF",
231
+ border: "#D1D1D6",
232
+ text: "#000000",
233
+ placeholder: "#8E8E93",
234
+ focusBorder: "#007AFF",
235
+ disabledBackground: "#F2F2F7",
236
+ disabledText: "#8E8E93"
237
+ },
238
+ spacing: {
239
+ padding: 8,
240
+ borderWidth: 1
241
+ },
242
+ borderRadius: 4,
243
+ fontSize: 16,
244
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
245
+ transition: "border-color 0.2s ease, background-color 0.2s ease"
246
+ };
247
+ class C extends E {
248
+ constructor(e) {
249
+ super(), l(this, "type", "component"), l(this, "id"), l(this, "props"), l(this, "theme", c), l(this, "inputElement", null), l(this, "handleInput", (s) => {
250
+ const i = s.target.value;
251
+ this.setText(i), this.props.onChange && this.props.onChange(i);
252
+ }), l(this, "handleKeyDown", (s) => {
253
+ s.key === "Enter" && this.props.onSubmit && (s.preventDefault(), this.props.onSubmit(this.getText()));
254
+ }), l(this, "handleFocus", () => {
255
+ this.props.onFocus && this.props.onFocus(), this.inputElement && (this.inputElement.style.borderColor = this.theme.colors.focusBorder);
256
+ }), l(this, "handleBlur", () => {
257
+ this.props.onBlur && this.props.onBlur(), this.inputElement && (this.inputElement.style.borderColor = this.theme.colors.border);
258
+ }), this.props = e, this.id = `basicinput-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
259
+ }
260
+ resolveValue(e) {
261
+ return h(e) ? e() : e;
262
+ }
263
+ getText() {
264
+ return this.resolveValue(this.props.text);
265
+ }
266
+ setText(e) {
267
+ this.props.setText && this.props.setText(e);
268
+ }
269
+ getPlaceholder() {
270
+ return this.resolveValue(this.props.placeholder || "");
271
+ }
272
+ getInputType() {
273
+ return this.resolveValue(this.props.inputType || "text");
274
+ }
275
+ isDisabled() {
276
+ return this.resolveValue(this.props.disabled || !1);
277
+ }
278
+ isReadonly() {
279
+ return this.resolveValue(this.props.readonly || !1);
280
+ }
281
+ getInputStyles() {
282
+ const e = this.isDisabled();
283
+ return {
284
+ width: "100%",
285
+ padding: `${this.theme.spacing.padding}px`,
286
+ border: `${this.theme.spacing.borderWidth}px solid ${this.theme.colors.border}`,
287
+ borderRadius: `${this.theme.borderRadius}px`,
288
+ backgroundColor: e ? this.theme.colors.disabledBackground : this.theme.colors.background,
289
+ color: e ? this.theme.colors.disabledText : this.theme.colors.text,
290
+ fontSize: `${this.theme.fontSize}px`,
291
+ fontFamily: this.theme.fontFamily,
292
+ outline: "none",
293
+ transition: this.theme.transition,
294
+ cursor: e ? "not-allowed" : "text"
295
+ };
296
+ }
297
+ render() {
298
+ const e = this.getText(), s = this.getPlaceholder(), r = this.getInputType(), i = this.isDisabled(), n = this.isReadonly(), a = ["tachui-basic-input"], x = this.createClassString(this.props, a), m = p("input", {
299
+ id: this.id,
300
+ className: x,
301
+ type: r,
302
+ value: e,
303
+ placeholder: s,
304
+ disabled: i,
305
+ readonly: n,
306
+ "aria-label": this.props.accessibilityLabel,
307
+ "aria-describedby": this.props.accessibilityHint ? `${this.id}-hint` : void 0,
308
+ style: this.getInputStyles(),
309
+ oninput: this.handleInput,
310
+ onkeydown: this.handleKeyDown,
311
+ onfocus: this.handleFocus,
312
+ onblur: this.handleBlur
313
+ });
314
+ return this.inputElement = m.element, u(() => {
315
+ if (this.inputElement && h(this.props.text)) {
316
+ const f = this.getText();
317
+ this.inputElement.value !== f && (this.inputElement.value = f);
318
+ }
319
+ }), u(() => {
320
+ this.inputElement && (h(this.props.placeholder) || h(this.props.inputType) || h(this.props.disabled)) && (h(this.props.placeholder) && (this.inputElement.placeholder = this.getPlaceholder()), h(this.props.inputType) && (this.inputElement.type = this.getInputType()), h(this.props.disabled) && (this.inputElement.disabled = this.isDisabled()));
321
+ }), [m];
322
+ }
323
+ }
324
+ function D(t) {
325
+ return y(new C(t));
326
+ }
327
+ const $ = {
328
+ /**
329
+ * Create a search input
330
+ */
331
+ search(t, e, s) {
332
+ return {
333
+ text: t,
334
+ setText: e,
335
+ inputType: "search",
336
+ placeholder: "Search...",
337
+ onSubmit: s
338
+ };
339
+ },
340
+ /**
341
+ * Create an email input
342
+ */
343
+ email(t, e) {
344
+ return {
345
+ text: t,
346
+ setText: e,
347
+ inputType: "email",
348
+ placeholder: "Enter email address"
349
+ };
350
+ },
351
+ /**
352
+ * Create a password input
353
+ */
354
+ password(t, e) {
355
+ return {
356
+ text: t,
357
+ setText: e,
358
+ inputType: "password",
359
+ placeholder: "Enter password"
360
+ };
361
+ },
362
+ /**
363
+ * Create a phone number input
364
+ */
365
+ phone(t, e) {
366
+ return {
367
+ text: t,
368
+ setText: e,
369
+ inputType: "tel",
370
+ placeholder: "Enter phone number"
371
+ };
372
+ }
373
+ }, _ = {
374
+ theme: c,
375
+ /**
376
+ * Create a custom theme
377
+ */
378
+ createTheme(t) {
379
+ return { ...c, ...t };
380
+ }
381
+ };
382
+ export {
383
+ d as B,
384
+ F as a,
385
+ B as b,
386
+ I as c,
387
+ D as d,
388
+ _ as e,
389
+ $ as f
390
+ };