@geektech/one 0.0.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/dist/index.js ADDED
@@ -0,0 +1,1476 @@
1
+ // lib/button/OneButton.ts
2
+ import { Component } from "@geektech/tsone";
3
+
4
+ // lib/styles/shared.ts
5
+ var ONE_THEME_DEFAULTS = {
6
+ colorPrimary: "#5fd956",
7
+ colorPrimaryHover: "#4bc944",
8
+ colorDanger: "#b83232",
9
+ colorDangerHover: "#9f2d2d",
10
+ colorSurface: "#ffffff",
11
+ colorText: "#162018",
12
+ colorMuted: "#647268",
13
+ colorBorder: "#d9e8d6",
14
+ colorFocus: "#2f7c39",
15
+ radiusSm: "4px",
16
+ radiusMd: "8px",
17
+ spaceXs: "4px",
18
+ spaceSm: "8px",
19
+ spaceMd: "12px",
20
+ spaceLg: "16px",
21
+ fontSizeSm: "12px",
22
+ fontSizeMd: "14px",
23
+ fontSizeLg: "16px",
24
+ shadowCard: "0 12px 30px rgba(32, 74, 38, 0.1)",
25
+ fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
26
+ };
27
+ var ONE_SIZES = ["sm", "md", "lg"];
28
+ function normalizeOneSize(value) {
29
+ return ONE_SIZES.includes(value) ? value : "md";
30
+ }
31
+
32
+ // lib/button/OneButton.ts
33
+ var ONE_BUTTON_VARIANTS = [
34
+ "primary",
35
+ "secondary",
36
+ "danger"
37
+ ];
38
+ var ONE_BUTTON_TYPES = [
39
+ "button",
40
+ "submit",
41
+ "reset"
42
+ ];
43
+ var ONE_BUTTON_STYLES = [
44
+ {
45
+ name: "one-button-base",
46
+ selector: ".one-button",
47
+ properties: {
48
+ display: "inline-flex",
49
+ alignItems: "center",
50
+ justifyContent: "center",
51
+ gap: `var(--one-button-gap, var(--one-space-sm, ${ONE_THEME_DEFAULTS.spaceSm}))`,
52
+ border: "1px solid transparent",
53
+ borderRadius: `var(--one-radius-md, ${ONE_THEME_DEFAULTS.radiusMd})`,
54
+ fontFamily: `var(--one-font-family, ${ONE_THEME_DEFAULTS.fontFamily})`,
55
+ fontWeight: "500",
56
+ lineHeight: "1.5",
57
+ cursor: "pointer",
58
+ transition: "150ms ease"
59
+ }
60
+ },
61
+ {
62
+ name: "one-button-primary",
63
+ selector: ".one-button--primary",
64
+ properties: {
65
+ color: `var(--one-color-primary-contrast, ${ONE_THEME_DEFAULTS.colorText})`,
66
+ backgroundColor: `var(--one-color-primary, ${ONE_THEME_DEFAULTS.colorPrimary})`
67
+ },
68
+ hover: {
69
+ backgroundColor: `var(--one-color-primary-hover, ${ONE_THEME_DEFAULTS.colorPrimaryHover})`
70
+ }
71
+ },
72
+ {
73
+ name: "one-button-secondary",
74
+ selector: ".one-button--secondary",
75
+ properties: {
76
+ color: `var(--one-color-secondary-contrast, ${ONE_THEME_DEFAULTS.colorText})`,
77
+ backgroundColor: `var(--one-color-secondary, ${ONE_THEME_DEFAULTS.colorSurface})`
78
+ },
79
+ hover: {
80
+ backgroundColor: `var(--one-color-secondary-hover, ${ONE_THEME_DEFAULTS.colorBorder})`
81
+ }
82
+ },
83
+ {
84
+ name: "one-button-danger",
85
+ selector: ".one-button--danger",
86
+ properties: {
87
+ color: `var(--one-color-danger-contrast, ${ONE_THEME_DEFAULTS.colorSurface})`,
88
+ backgroundColor: `var(--one-color-danger, ${ONE_THEME_DEFAULTS.colorDanger})`
89
+ },
90
+ hover: {
91
+ backgroundColor: `var(--one-color-danger-hover, ${ONE_THEME_DEFAULTS.colorDangerHover})`
92
+ }
93
+ },
94
+ {
95
+ name: "one-button-sm",
96
+ selector: ".one-button--sm",
97
+ properties: {
98
+ minHeight: "32px",
99
+ padding: `var(--one-button-padding-sm, ${ONE_THEME_DEFAULTS.spaceXs} ${ONE_THEME_DEFAULTS.spaceMd})`,
100
+ fontSize: `var(--one-font-size-sm, ${ONE_THEME_DEFAULTS.fontSizeSm})`
101
+ }
102
+ },
103
+ {
104
+ name: "one-button-md",
105
+ selector: ".one-button--md",
106
+ properties: {
107
+ minHeight: "40px",
108
+ padding: `var(--one-button-padding-md, ${ONE_THEME_DEFAULTS.spaceSm} ${ONE_THEME_DEFAULTS.spaceLg})`,
109
+ fontSize: `var(--one-font-size-md, ${ONE_THEME_DEFAULTS.fontSizeMd})`
110
+ }
111
+ },
112
+ {
113
+ name: "one-button-lg",
114
+ selector: ".one-button--lg",
115
+ properties: {
116
+ minHeight: "48px",
117
+ padding: `var(--one-button-padding-lg, ${ONE_THEME_DEFAULTS.spaceMd} ${ONE_THEME_DEFAULTS.spaceLg})`,
118
+ fontSize: `var(--one-font-size-lg, ${ONE_THEME_DEFAULTS.fontSizeLg})`
119
+ }
120
+ },
121
+ {
122
+ name: "one-button-disabled",
123
+ selector: ".one-button:disabled",
124
+ properties: {
125
+ opacity: "0.5",
126
+ cursor: "not-allowed"
127
+ }
128
+ },
129
+ {
130
+ name: "one-button-focus-visible",
131
+ selector: ".one-button:focus-visible",
132
+ properties: {
133
+ outline: `2px solid var(--one-color-focus, ${ONE_THEME_DEFAULTS.colorFocus})`,
134
+ outlineOffset: "2px"
135
+ }
136
+ },
137
+ {
138
+ name: "one-button-spinner",
139
+ selector: ".one-button__spinner",
140
+ properties: {
141
+ width: "1em",
142
+ height: "1em",
143
+ border: "2px solid currentColor",
144
+ borderRightColor: "transparent",
145
+ borderRadius: "50%"
146
+ }
147
+ }
148
+ ];
149
+ function normalizeButtonVariant(value) {
150
+ return ONE_BUTTON_VARIANTS.includes(value) ? value : "primary";
151
+ }
152
+ function normalizeButtonType(value) {
153
+ return ONE_BUTTON_TYPES.includes(value) ? value : "button";
154
+ }
155
+
156
+ class OneButton extends Component {
157
+ initState() {
158
+ return {};
159
+ }
160
+ initStyles() {
161
+ ONE_BUTTON_STYLES.forEach(({ name, ...style }) => {
162
+ this.styleManager.addStyle(name, style);
163
+ });
164
+ }
165
+ render() {
166
+ const variant = normalizeButtonVariant(this.props.variant);
167
+ const size = normalizeOneSize(this.props.size);
168
+ const disabled = this.props.disabled === true || this.props.loading === true;
169
+ return {
170
+ tag: "button",
171
+ props: {
172
+ className: `one-button one-button--${variant} one-button--${size}`,
173
+ type: normalizeButtonType(this.props.type),
174
+ disabled,
175
+ "aria-busy": this.props.loading === true ? "true" : undefined
176
+ },
177
+ listeners: {
178
+ click: (event) => {
179
+ if (!disabled && event instanceof MouseEvent) {
180
+ this.emit("click", event);
181
+ }
182
+ }
183
+ },
184
+ children: [
185
+ ...this.props.loading ? [
186
+ {
187
+ tag: "span",
188
+ props: {
189
+ className: "one-button__spinner",
190
+ "aria-hidden": "true"
191
+ }
192
+ }
193
+ ] : [],
194
+ ...this.props.children ?? []
195
+ ]
196
+ };
197
+ }
198
+ }
199
+ // lib/input/OneInput.ts
200
+ import { Component as Component2 } from "@geektech/tsone";
201
+
202
+ // lib/form/context.ts
203
+ var ONE_FORM_MODEL_KEY = Symbol("one-form-model");
204
+ var ONE_FORM_FIELD_KEY = Symbol("one-form-field");
205
+
206
+ // lib/input/OneInput.ts
207
+ var ONE_INPUT_STYLES = [
208
+ {
209
+ name: "one-input-base",
210
+ selector: ".one-input",
211
+ properties: {
212
+ boxSizing: "border-box",
213
+ width: "100%",
214
+ border: `1px solid var(--one-input-border-color, var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder}))`,
215
+ borderRadius: `var(--one-radius-md, ${ONE_THEME_DEFAULTS.radiusMd})`,
216
+ color: `var(--one-input-color, var(--one-color-text, ${ONE_THEME_DEFAULTS.colorText}))`,
217
+ backgroundColor: `var(--one-input-background, var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface}))`,
218
+ fontFamily: `var(--one-font-family, ${ONE_THEME_DEFAULTS.fontFamily})`,
219
+ lineHeight: "1.5",
220
+ transition: "150ms ease"
221
+ }
222
+ },
223
+ {
224
+ name: "one-input-sm",
225
+ selector: ".one-input--sm",
226
+ properties: {
227
+ minHeight: "32px",
228
+ padding: `var(--one-input-padding-sm, ${ONE_THEME_DEFAULTS.spaceXs} ${ONE_THEME_DEFAULTS.spaceSm})`,
229
+ fontSize: `var(--one-font-size-sm, ${ONE_THEME_DEFAULTS.fontSizeSm})`
230
+ }
231
+ },
232
+ {
233
+ name: "one-input-md",
234
+ selector: ".one-input--md",
235
+ properties: {
236
+ minHeight: "40px",
237
+ padding: `var(--one-input-padding-md, ${ONE_THEME_DEFAULTS.spaceSm} ${ONE_THEME_DEFAULTS.spaceMd})`,
238
+ fontSize: `var(--one-font-size-md, ${ONE_THEME_DEFAULTS.fontSizeMd})`
239
+ }
240
+ },
241
+ {
242
+ name: "one-input-lg",
243
+ selector: ".one-input--lg",
244
+ properties: {
245
+ minHeight: "48px",
246
+ padding: `var(--one-input-padding-lg, ${ONE_THEME_DEFAULTS.spaceMd} ${ONE_THEME_DEFAULTS.spaceLg})`,
247
+ fontSize: `var(--one-font-size-lg, ${ONE_THEME_DEFAULTS.fontSizeLg})`
248
+ }
249
+ },
250
+ {
251
+ name: "one-input-invalid",
252
+ selector: ".one-input--invalid",
253
+ properties: {
254
+ borderColor: `var(--one-color-danger, ${ONE_THEME_DEFAULTS.colorDanger})`
255
+ }
256
+ },
257
+ {
258
+ name: "one-input-disabled",
259
+ selector: ".one-input:disabled",
260
+ properties: {
261
+ opacity: "0.5",
262
+ cursor: "not-allowed",
263
+ backgroundColor: `var(--one-input-disabled-background, var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface}))`
264
+ }
265
+ },
266
+ {
267
+ name: "one-input-focus-visible",
268
+ selector: ".one-input:focus-visible",
269
+ properties: {
270
+ outline: `2px solid var(--one-color-focus, ${ONE_THEME_DEFAULTS.colorFocus})`,
271
+ outlineOffset: "2px",
272
+ borderColor: `var(--one-input-focus-border-color, var(--one-color-focus, ${ONE_THEME_DEFAULTS.colorFocus}))`
273
+ }
274
+ }
275
+ ];
276
+
277
+ class OneInput extends Component2 {
278
+ fieldContext;
279
+ unsubscribe;
280
+ initState() {
281
+ return { internalValue: this.props.defaultValue ?? "", revision: 0 };
282
+ }
283
+ initStyles() {
284
+ ONE_INPUT_STYLES.forEach(({ name, ...style }) => {
285
+ this.styleManager.addStyle(name, style);
286
+ });
287
+ }
288
+ render() {
289
+ this.state.revision;
290
+ const size = normalizeOneSize(this.props.size);
291
+ const fieldContext = this.fieldContext;
292
+ const fieldErrors = fieldContext?.model.getErrors(fieldContext.name) ?? [];
293
+ const value = fieldContext ? fieldContext.model.getValue(fieldContext.name) : this.props.value ?? this.state.internalValue;
294
+ const classNames = [
295
+ "one-input",
296
+ ...this.props.invalid || fieldErrors.length > 0 ? ["one-input--invalid"] : [],
297
+ `one-input--${size}`
298
+ ];
299
+ return {
300
+ tag: "input",
301
+ props: {
302
+ className: classNames.join(" "),
303
+ value: typeof value === "string" ? value : "",
304
+ type: this.props.type,
305
+ name: fieldContext?.name ?? this.props.name,
306
+ id: fieldContext?.controlId,
307
+ placeholder: this.props.placeholder,
308
+ disabled: this.props.disabled === true,
309
+ readonly: this.props.readonly === true,
310
+ required: this.props.required === true,
311
+ "aria-invalid": this.props.invalid || fieldErrors.length > 0 ? "true" : undefined,
312
+ "aria-describedby": fieldContext?.describedBy,
313
+ "aria-label": this.props.ariaLabel
314
+ },
315
+ listeners: {
316
+ input: (event) => this.emitValue("input", event),
317
+ change: (event) => this.emitValue("change", event)
318
+ }
319
+ };
320
+ }
321
+ onUpdated() {
322
+ const input = this.getElement();
323
+ if (!this.fieldContext && this.props.value !== undefined && input instanceof HTMLInputElement) {
324
+ input.value = this.props.value;
325
+ }
326
+ }
327
+ beforeMount() {
328
+ this.fieldContext = this.inject(ONE_FORM_FIELD_KEY);
329
+ if (this.fieldContext) {
330
+ this.fieldContext.model.ensureValue(this.fieldContext.name, this.props.value ?? this.props.defaultValue ?? "");
331
+ }
332
+ }
333
+ onMounted() {
334
+ this.unsubscribe = this.fieldContext?.model.subscribe(() => {
335
+ this.state.revision += 1;
336
+ });
337
+ }
338
+ onUnmounted() {
339
+ this.unsubscribe?.();
340
+ this.unsubscribe = undefined;
341
+ this.fieldContext = undefined;
342
+ }
343
+ emitValue(eventName, event) {
344
+ const input = event.currentTarget;
345
+ if (!(input instanceof HTMLInputElement)) {
346
+ return;
347
+ }
348
+ const nextValue = input.value;
349
+ if (this.fieldContext) {
350
+ this.fieldContext.model.setValue(this.fieldContext.name, nextValue);
351
+ } else if (this.props.value === undefined) {
352
+ this.state.internalValue = nextValue;
353
+ }
354
+ this.emit(eventName, { value: nextValue, originalEvent: event });
355
+ if (!this.fieldContext && this.props.value !== undefined) {
356
+ input.value = this.props.value;
357
+ }
358
+ }
359
+ }
360
+ // lib/form/model.ts
361
+ function hasOwn(object, key) {
362
+ return Object.prototype.hasOwnProperty.call(object, key);
363
+ }
364
+ function cloneFieldValue(value) {
365
+ return Array.isArray(value) ? [...value] : value;
366
+ }
367
+ function cloneValues(values) {
368
+ return Object.fromEntries(Object.entries(values).map(([name, value]) => [name, cloneFieldValue(value)]));
369
+ }
370
+ function isRequiredValue(value) {
371
+ return value !== undefined && value !== "" && value !== false && (!Array.isArray(value) || value.length > 0);
372
+ }
373
+ function getLength(value) {
374
+ return typeof value === "string" || Array.isArray(value) ? value.length : undefined;
375
+ }
376
+ function defaultRuleMessage(rule) {
377
+ if (rule.required) {
378
+ return "此字段为必填项";
379
+ }
380
+ if (rule.minLength !== undefined) {
381
+ return `长度不能少于 ${rule.minLength}`;
382
+ }
383
+ if (rule.maxLength !== undefined) {
384
+ return `长度不能超过 ${rule.maxLength}`;
385
+ }
386
+ if (rule.pattern) {
387
+ return "格式不正确";
388
+ }
389
+ return "校验失败";
390
+ }
391
+
392
+ class OneFormModel {
393
+ initialValues;
394
+ rules;
395
+ values = {};
396
+ errors = new Map;
397
+ fields = new Map;
398
+ subscribers = new Set;
399
+ constructor(initialValues = {}, rules = {}) {
400
+ this.initialValues = cloneValues(initialValues);
401
+ this.rules = Object.fromEntries(Object.entries(rules).map(([name, fieldRules]) => [
402
+ name,
403
+ fieldRules ? fieldRules.map((rule) => ({ ...rule })) : []
404
+ ]));
405
+ }
406
+ ensureValue(name, fallback) {
407
+ if (hasOwn(this.values, name)) {
408
+ return;
409
+ }
410
+ this.values[name] = cloneFieldValue(hasOwn(this.initialValues, name) ? this.initialValues[name] : fallback);
411
+ this.notify();
412
+ }
413
+ setValue(name, value) {
414
+ this.values[name] = cloneFieldValue(value);
415
+ this.notify();
416
+ }
417
+ getValue(name) {
418
+ return cloneFieldValue(this.values[name]);
419
+ }
420
+ getValues() {
421
+ return cloneValues(Object.fromEntries([...this.fields.keys()].map((name) => [name, this.values[name]])));
422
+ }
423
+ getErrors(name) {
424
+ return [...this.errors.get(name) ?? []];
425
+ }
426
+ validate() {
427
+ this.errors.clear();
428
+ const values = this.getValues();
429
+ this.fields.forEach((_, name) => {
430
+ const messages = this.validateField(name, values);
431
+ if (messages.length > 0) {
432
+ this.errors.set(name, messages);
433
+ }
434
+ });
435
+ this.notify();
436
+ return this.getValidationResult();
437
+ }
438
+ reset() {
439
+ this.errors.clear();
440
+ this.fields.forEach((_, name) => {
441
+ if (hasOwn(this.initialValues, name)) {
442
+ this.values[name] = cloneFieldValue(this.initialValues[name]);
443
+ } else {
444
+ delete this.values[name];
445
+ }
446
+ });
447
+ this.notify();
448
+ }
449
+ subscribe(listener) {
450
+ this.subscribers.add(listener);
451
+ return () => this.subscribers.delete(listener);
452
+ }
453
+ registerField(name, focus) {
454
+ if (!hasOwn(this.values, name) && hasOwn(this.initialValues, name)) {
455
+ this.values[name] = cloneFieldValue(this.initialValues[name]);
456
+ }
457
+ this.fields.set(name, focus);
458
+ return () => {
459
+ this.fields.delete(name);
460
+ this.errors.delete(name);
461
+ delete this.values[name];
462
+ this.notify();
463
+ };
464
+ }
465
+ focusFirstInvalidField() {
466
+ for (const [name, focus] of this.fields) {
467
+ if (this.errors.has(name)) {
468
+ focus();
469
+ return true;
470
+ }
471
+ }
472
+ return false;
473
+ }
474
+ validateField(name, values) {
475
+ const value = this.values[name];
476
+ const messages = [];
477
+ this.rules[name]?.forEach((rule) => {
478
+ if (rule.required && !isRequiredValue(value)) {
479
+ messages.push(rule.message ?? defaultRuleMessage(rule));
480
+ }
481
+ const length = getLength(value);
482
+ if (rule.minLength !== undefined && length !== undefined && length < rule.minLength) {
483
+ messages.push(rule.message ?? defaultRuleMessage(rule));
484
+ }
485
+ if (rule.maxLength !== undefined && length !== undefined && length > rule.maxLength) {
486
+ messages.push(rule.message ?? defaultRuleMessage(rule));
487
+ }
488
+ if (rule.pattern && typeof value === "string" && !rule.pattern.test(value)) {
489
+ messages.push(rule.message ?? defaultRuleMessage(rule));
490
+ }
491
+ if (rule.validator) {
492
+ try {
493
+ const message = rule.validator(cloneFieldValue(value), values);
494
+ if (message) {
495
+ messages.push(message);
496
+ }
497
+ } catch {
498
+ messages.push("校验器执行失败");
499
+ }
500
+ }
501
+ });
502
+ return messages;
503
+ }
504
+ getValidationResult() {
505
+ const errors = Object.fromEntries([...this.errors.entries()].map(([name, messages]) => [name, [...messages]]));
506
+ return { valid: this.errors.size === 0, errors };
507
+ }
508
+ notify() {
509
+ [...this.subscribers].forEach((listener) => listener());
510
+ }
511
+ }
512
+ // lib/form/OneForm.ts
513
+ import { Component as Component3, slot } from "@geektech/tsone";
514
+ var ONE_FORM_STYLES = [
515
+ {
516
+ name: "one-form-base",
517
+ selector: ".one-form",
518
+ properties: {
519
+ display: "grid",
520
+ gap: "16px"
521
+ }
522
+ }
523
+ ];
524
+
525
+ class OneForm extends Component3 {
526
+ model;
527
+ constructor(props = {}) {
528
+ super(props);
529
+ this.model = new OneFormModel(props.initialValues, props.rules);
530
+ this.provide(ONE_FORM_MODEL_KEY, this.model);
531
+ }
532
+ validate() {
533
+ return this.model.validate();
534
+ }
535
+ reset() {
536
+ this.model.reset();
537
+ }
538
+ getValues() {
539
+ return this.model.getValues();
540
+ }
541
+ initState() {
542
+ return { revision: 0 };
543
+ }
544
+ initStyles() {
545
+ ONE_FORM_STYLES.forEach(({ name, ...style }) => {
546
+ this.styleManager.addStyle(name, style);
547
+ });
548
+ }
549
+ render() {
550
+ return {
551
+ tag: "form",
552
+ props: { className: "one-form", novalidate: true },
553
+ children: [slot("default")],
554
+ listeners: {
555
+ submit: (event) => this.handleSubmit(event),
556
+ reset: (event) => this.handleReset(event)
557
+ }
558
+ };
559
+ }
560
+ handleSubmit(event) {
561
+ event.preventDefault();
562
+ const result = this.model.validate();
563
+ if (!result.valid) {
564
+ this.model.focusFirstInvalidField();
565
+ return;
566
+ }
567
+ this.emit("submit", { values: this.model.getValues() });
568
+ }
569
+ handleReset(event) {
570
+ event.preventDefault();
571
+ this.model.reset();
572
+ }
573
+ }
574
+ // lib/form/OneFormItem.ts
575
+ import { Component as Component4, slot as slot2 } from "@geektech/tsone";
576
+ var ONE_FORM_ITEM_STYLES = [
577
+ {
578
+ name: "one-form-item-base",
579
+ selector: ".one-form-item",
580
+ properties: {
581
+ display: "grid",
582
+ gap: "6px"
583
+ }
584
+ },
585
+ {
586
+ name: "one-form-item-label",
587
+ selector: ".one-form-item__label",
588
+ properties: { fontWeight: "500" }
589
+ },
590
+ {
591
+ name: "one-form-item-description",
592
+ selector: ".one-form-item__description",
593
+ properties: { color: "var(--one-color-muted, #647268)", fontSize: "12px" }
594
+ },
595
+ {
596
+ name: "one-form-item-error",
597
+ selector: ".one-form-item__error",
598
+ properties: { color: "var(--one-color-danger, #b83232)", fontSize: "12px" }
599
+ }
600
+ ];
601
+ function normalizeName(name) {
602
+ return name.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "field";
603
+ }
604
+
605
+ class OneFormItem extends Component4 {
606
+ model;
607
+ unsubscribe;
608
+ unregister;
609
+ fieldSignature = "";
610
+ initState() {
611
+ return { revision: 0 };
612
+ }
613
+ initStyles() {
614
+ ONE_FORM_ITEM_STYLES.forEach(({ name, ...style }) => {
615
+ this.styleManager.addStyle(name, style);
616
+ });
617
+ }
618
+ beforeMount() {
619
+ const model = this.inject(ONE_FORM_MODEL_KEY);
620
+ if (!model) {
621
+ throw new Error("OneFormItem requires OneForm");
622
+ }
623
+ this.model = model;
624
+ this.fieldSignature = this.getFieldSignature(model);
625
+ this.provide(ONE_FORM_FIELD_KEY, this.getFieldContext(model));
626
+ this.unregister = model.registerField(this.props.name, () => {
627
+ const element = this.getElement();
628
+ if (!(element instanceof HTMLElement)) {
629
+ return;
630
+ }
631
+ element.querySelector("input, button")?.focus();
632
+ });
633
+ }
634
+ onMounted() {
635
+ this.unsubscribe = this.model?.subscribe(() => {
636
+ if (!this.model) {
637
+ return;
638
+ }
639
+ const fieldSignature = this.getFieldSignature(this.model);
640
+ if (fieldSignature === this.fieldSignature) {
641
+ return;
642
+ }
643
+ this.fieldSignature = fieldSignature;
644
+ this.state.revision += 1;
645
+ });
646
+ }
647
+ onUnmounted() {
648
+ this.unsubscribe?.();
649
+ this.unsubscribe = undefined;
650
+ this.unregister?.();
651
+ this.unregister = undefined;
652
+ this.model = undefined;
653
+ }
654
+ render() {
655
+ this.state.revision;
656
+ const model = this.model;
657
+ const ids = this.getIds();
658
+ const errors = model?.getErrors(this.props.name) ?? [];
659
+ return {
660
+ tag: "div",
661
+ props: { className: "one-form-item" },
662
+ children: [
663
+ ...this.props.label ? [
664
+ {
665
+ tag: "label",
666
+ props: { className: "one-form-item__label", for: ids.control },
667
+ children: [this.props.label]
668
+ }
669
+ ] : [],
670
+ slot2("default"),
671
+ ...this.props.description ? [
672
+ {
673
+ tag: "div",
674
+ props: {
675
+ id: ids.description,
676
+ className: "one-form-item__description"
677
+ },
678
+ children: [this.props.description]
679
+ }
680
+ ] : [],
681
+ ...errors.length > 0 ? [
682
+ {
683
+ tag: "div",
684
+ props: {
685
+ id: ids.error,
686
+ className: "one-form-item__error",
687
+ role: "alert"
688
+ },
689
+ children: [errors.join(" ")]
690
+ }
691
+ ] : []
692
+ ]
693
+ };
694
+ }
695
+ getFieldContext(model) {
696
+ const ids = this.getIds();
697
+ return {
698
+ name: this.props.name,
699
+ controlId: ids.control,
700
+ describedBy: this.props.description ? `${ids.description} ${ids.error}` : ids.error,
701
+ model
702
+ };
703
+ }
704
+ getIds() {
705
+ const name = normalizeName(this.props.name);
706
+ return {
707
+ control: `one-form-${name}-control`,
708
+ description: `one-form-${name}-description`,
709
+ error: `one-form-${name}-error`
710
+ };
711
+ }
712
+ getFieldSignature(model) {
713
+ return JSON.stringify([
714
+ model.getValue(this.props.name),
715
+ model.getErrors(this.props.name)
716
+ ]);
717
+ }
718
+ }
719
+ // lib/checkbox/OneCheckbox.ts
720
+ import { Component as Component5, slot as slot3 } from "@geektech/tsone";
721
+ var ONE_CHECKBOX_STYLES = [
722
+ {
723
+ name: "one-checkbox-base",
724
+ selector: ".one-checkbox",
725
+ properties: {
726
+ display: "inline-flex",
727
+ gap: "8px",
728
+ alignItems: "center",
729
+ color: `var(--one-color-text, ${ONE_THEME_DEFAULTS.colorText})`
730
+ }
731
+ },
732
+ {
733
+ name: "one-checkbox-input",
734
+ selector: ".one-checkbox__input",
735
+ properties: {
736
+ accentColor: `var(--one-color-primary, ${ONE_THEME_DEFAULTS.colorPrimary})`
737
+ }
738
+ },
739
+ {
740
+ name: "one-checkbox-invalid",
741
+ selector: ".one-checkbox--invalid",
742
+ properties: {
743
+ color: `var(--one-color-danger, ${ONE_THEME_DEFAULTS.colorDanger})`
744
+ }
745
+ },
746
+ {
747
+ name: "one-checkbox-disabled",
748
+ selector: ".one-checkbox--disabled",
749
+ properties: { opacity: "0.5", cursor: "not-allowed" }
750
+ }
751
+ ];
752
+
753
+ class OneCheckbox extends Component5 {
754
+ fieldContext;
755
+ unsubscribe;
756
+ initState() {
757
+ return { internalChecked: this.props.defaultChecked ?? false, revision: 0 };
758
+ }
759
+ initStyles() {
760
+ ONE_CHECKBOX_STYLES.forEach(({ name, ...style }) => this.styleManager.addStyle(name, style));
761
+ }
762
+ beforeMount() {
763
+ this.fieldContext = this.inject(ONE_FORM_FIELD_KEY);
764
+ this.fieldContext?.model.ensureValue(this.fieldContext.name, this.props.checked ?? this.props.defaultChecked ?? false);
765
+ }
766
+ onMounted() {
767
+ this.unsubscribe = this.fieldContext?.model.subscribe(() => {
768
+ this.state.revision += 1;
769
+ });
770
+ }
771
+ onUnmounted() {
772
+ this.unsubscribe?.();
773
+ this.unsubscribe = undefined;
774
+ this.fieldContext = undefined;
775
+ }
776
+ onUpdated() {
777
+ const element = this.getElement();
778
+ const input = element instanceof HTMLElement ? element.querySelector("input") : null;
779
+ if (input instanceof HTMLInputElement)
780
+ input.checked = this.getChecked();
781
+ }
782
+ render() {
783
+ this.state.revision;
784
+ const checked = this.getChecked();
785
+ const invalid = this.props.invalid || (this.fieldContext?.model.getErrors(this.fieldContext.name).length ?? 0) > 0;
786
+ return {
787
+ tag: "label",
788
+ props: {
789
+ className: [
790
+ "one-checkbox",
791
+ ...invalid ? ["one-checkbox--invalid"] : [],
792
+ ...this.props.disabled ? ["one-checkbox--disabled"] : []
793
+ ].join(" ")
794
+ },
795
+ children: [
796
+ {
797
+ tag: "input",
798
+ props: {
799
+ className: "one-checkbox__input",
800
+ type: "checkbox",
801
+ checked,
802
+ name: this.fieldContext?.name ?? this.props.name,
803
+ disabled: this.props.disabled === true,
804
+ required: this.props.required === true,
805
+ id: this.fieldContext?.controlId,
806
+ "aria-label": this.props.ariaLabel,
807
+ "aria-invalid": invalid ? "true" : undefined,
808
+ "aria-describedby": this.fieldContext?.describedBy
809
+ },
810
+ listeners: {
811
+ input: (event) => this.emitChecked("input", event),
812
+ change: (event) => this.emitChecked("change", event)
813
+ }
814
+ },
815
+ slot3("default")
816
+ ]
817
+ };
818
+ }
819
+ getChecked() {
820
+ const value = this.fieldContext?.model.getValue(this.fieldContext.name);
821
+ return typeof value === "boolean" ? value : this.props.checked ?? this.state.internalChecked;
822
+ }
823
+ emitChecked(eventName, event) {
824
+ const input = event.currentTarget;
825
+ if (!(input instanceof HTMLInputElement) || this.props.disabled)
826
+ return;
827
+ const value = input.checked;
828
+ if (this.fieldContext)
829
+ this.fieldContext.model.setValue(this.fieldContext.name, value);
830
+ else if (this.props.checked === undefined)
831
+ this.state.internalChecked = value;
832
+ this.emit(eventName, {
833
+ value,
834
+ originalEvent: event
835
+ });
836
+ if (!this.fieldContext && this.props.checked !== undefined)
837
+ input.checked = this.props.checked;
838
+ }
839
+ }
840
+ // lib/checkbox/OneCheckboxGroup.ts
841
+ import { Component as Component6 } from "@geektech/tsone";
842
+ class OneCheckboxGroup extends Component6 {
843
+ fieldContext;
844
+ unsubscribe;
845
+ initState() {
846
+ return { internalValue: [...this.props.defaultValue ?? []], revision: 0 };
847
+ }
848
+ initStyles() {}
849
+ beforeMount() {
850
+ this.fieldContext = this.inject(ONE_FORM_FIELD_KEY);
851
+ this.fieldContext?.model.ensureValue(this.fieldContext.name, this.props.value ?? this.props.defaultValue ?? []);
852
+ }
853
+ onMounted() {
854
+ this.unsubscribe = this.fieldContext?.model.subscribe(() => {
855
+ this.state.revision += 1;
856
+ });
857
+ }
858
+ onUnmounted() {
859
+ this.unsubscribe?.();
860
+ this.unsubscribe = undefined;
861
+ this.fieldContext = undefined;
862
+ }
863
+ render() {
864
+ this.state.revision;
865
+ const values = this.getValue();
866
+ const invalid = this.props.invalid || (this.fieldContext?.model.getErrors(this.fieldContext.name).length ?? 0) > 0;
867
+ return {
868
+ tag: "div",
869
+ props: {
870
+ className: "one-checkbox-group",
871
+ role: "group",
872
+ "aria-label": this.props.ariaLabel,
873
+ "aria-invalid": invalid ? "true" : undefined,
874
+ "aria-describedby": this.fieldContext?.describedBy
875
+ },
876
+ children: this.props.options.map((option) => ({
877
+ tag: "label",
878
+ props: { className: "one-checkbox-group__option" },
879
+ children: [
880
+ {
881
+ tag: "input",
882
+ props: {
883
+ type: "checkbox",
884
+ checked: values.includes(option.value),
885
+ disabled: this.props.disabled || option.disabled,
886
+ name: this.fieldContext?.name ?? this.props.name
887
+ },
888
+ listeners: { change: (event) => this.toggle(option, event) }
889
+ },
890
+ option.label
891
+ ]
892
+ }))
893
+ };
894
+ }
895
+ getValue() {
896
+ const value = this.fieldContext?.model.getValue(this.fieldContext.name);
897
+ return Array.isArray(value) ? [...value] : this.props.value ? [...this.props.value] : [...this.state.internalValue];
898
+ }
899
+ toggle(option, event) {
900
+ const input = event.currentTarget;
901
+ if (!(input instanceof HTMLInputElement) || this.props.disabled || option.disabled)
902
+ return;
903
+ const current = this.getValue();
904
+ const next = input.checked ? [...current, option.value] : current.filter((value) => value !== option.value);
905
+ const ordered = this.props.options.filter((item) => new Set(next).has(item.value)).map((item) => item.value);
906
+ if (this.fieldContext)
907
+ this.fieldContext.model.setValue(this.fieldContext.name, ordered);
908
+ else if (this.props.value === undefined)
909
+ this.state.internalValue = ordered;
910
+ this.emit("input", {
911
+ value: ordered,
912
+ originalEvent: event
913
+ });
914
+ this.emit("change", {
915
+ value: ordered,
916
+ originalEvent: event
917
+ });
918
+ }
919
+ }
920
+ // lib/switch/OneSwitch.ts
921
+ import { Component as Component7 } from "@geektech/tsone";
922
+ var ONE_SWITCH_STYLES = [
923
+ {
924
+ name: "one-switch-base",
925
+ selector: ".one-switch",
926
+ properties: {
927
+ display: "inline-flex",
928
+ alignItems: "center",
929
+ position: "relative",
930
+ width: "44px",
931
+ height: "24px",
932
+ borderRadius: "999px",
933
+ backgroundColor: `var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder})`,
934
+ cursor: "pointer",
935
+ transition: "150ms ease"
936
+ }
937
+ },
938
+ {
939
+ name: "one-switch-input",
940
+ selector: ".one-switch__input",
941
+ properties: {
942
+ position: "absolute",
943
+ inset: "0",
944
+ width: "100%",
945
+ height: "100%",
946
+ margin: "0",
947
+ opacity: "0",
948
+ cursor: "inherit"
949
+ }
950
+ },
951
+ {
952
+ name: "one-switch-thumb",
953
+ selector: ".one-switch__thumb",
954
+ properties: {
955
+ width: "18px",
956
+ height: "18px",
957
+ marginLeft: "3px",
958
+ borderRadius: "50%",
959
+ backgroundColor: `var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface})`,
960
+ boxShadow: "0 1px 3px rgba(22, 32, 24, 0.28)",
961
+ pointerEvents: "none",
962
+ transition: "150ms ease"
963
+ }
964
+ },
965
+ {
966
+ name: "one-switch-thumb-checked",
967
+ selector: ".one-switch--checked .one-switch__thumb",
968
+ properties: { transform: "translateX(20px)" }
969
+ },
970
+ {
971
+ name: "one-switch-checked",
972
+ selector: ".one-switch--checked",
973
+ properties: {
974
+ backgroundColor: `var(--one-color-primary, ${ONE_THEME_DEFAULTS.colorPrimary})`
975
+ }
976
+ },
977
+ {
978
+ name: "one-switch-invalid",
979
+ selector: ".one-switch--invalid",
980
+ properties: {
981
+ outline: `2px solid var(--one-color-danger, ${ONE_THEME_DEFAULTS.colorDanger})`
982
+ }
983
+ },
984
+ {
985
+ name: "one-switch-disabled",
986
+ selector: ".one-switch--disabled",
987
+ properties: { opacity: "0.5", cursor: "not-allowed" }
988
+ },
989
+ {
990
+ name: "one-switch-focus-visible",
991
+ selector: ".one-switch:has(.one-switch__input:focus-visible)",
992
+ properties: {
993
+ outline: `2px solid var(--one-color-focus, ${ONE_THEME_DEFAULTS.colorFocus})`,
994
+ outlineOffset: "2px"
995
+ }
996
+ }
997
+ ];
998
+
999
+ class OneSwitch extends Component7 {
1000
+ fieldContext;
1001
+ unsubscribe;
1002
+ initState() {
1003
+ return { internalChecked: this.props.defaultChecked ?? false, revision: 0 };
1004
+ }
1005
+ initStyles() {
1006
+ ONE_SWITCH_STYLES.forEach(({ name, ...style }) => this.styleManager.addStyle(name, style));
1007
+ }
1008
+ beforeMount() {
1009
+ this.fieldContext = this.inject(ONE_FORM_FIELD_KEY);
1010
+ this.fieldContext?.model.ensureValue(this.fieldContext.name, this.props.checked ?? this.props.defaultChecked ?? false);
1011
+ }
1012
+ onMounted() {
1013
+ this.unsubscribe = this.fieldContext?.model.subscribe(() => {
1014
+ this.state.revision += 1;
1015
+ });
1016
+ }
1017
+ onUnmounted() {
1018
+ this.unsubscribe?.();
1019
+ this.unsubscribe = undefined;
1020
+ this.fieldContext = undefined;
1021
+ }
1022
+ onUpdated() {
1023
+ const element = this.getElement();
1024
+ const input = element instanceof HTMLElement ? element.querySelector("input") : null;
1025
+ if (input instanceof HTMLInputElement)
1026
+ input.checked = this.getChecked();
1027
+ }
1028
+ render() {
1029
+ this.state.revision;
1030
+ const checked = this.getChecked();
1031
+ const invalid = this.props.invalid || (this.fieldContext?.model.getErrors(this.fieldContext.name).length ?? 0) > 0;
1032
+ return {
1033
+ tag: "label",
1034
+ props: {
1035
+ className: [
1036
+ "one-switch",
1037
+ ...checked ? ["one-switch--checked"] : [],
1038
+ ...invalid ? ["one-switch--invalid"] : [],
1039
+ ...this.props.disabled ? ["one-switch--disabled"] : []
1040
+ ].join(" "),
1041
+ role: "switch",
1042
+ "aria-checked": checked ? "true" : "false"
1043
+ },
1044
+ children: [
1045
+ {
1046
+ tag: "input",
1047
+ props: {
1048
+ className: "one-switch__input",
1049
+ type: "checkbox",
1050
+ checked,
1051
+ name: this.fieldContext?.name ?? this.props.name,
1052
+ disabled: this.props.disabled === true,
1053
+ required: this.props.required === true,
1054
+ id: this.fieldContext?.controlId,
1055
+ "aria-label": this.props.ariaLabel,
1056
+ "aria-invalid": invalid ? "true" : undefined,
1057
+ "aria-describedby": this.fieldContext?.describedBy
1058
+ },
1059
+ listeners: {
1060
+ input: (event) => this.emitChecked("input", event),
1061
+ change: (event) => this.emitChecked("change", event)
1062
+ }
1063
+ },
1064
+ {
1065
+ tag: "span",
1066
+ props: { className: "one-switch__thumb", "aria-hidden": "true" }
1067
+ }
1068
+ ]
1069
+ };
1070
+ }
1071
+ getChecked() {
1072
+ const value = this.fieldContext?.model.getValue(this.fieldContext.name);
1073
+ return typeof value === "boolean" ? value : this.props.checked ?? this.state.internalChecked;
1074
+ }
1075
+ emitChecked(eventName, event) {
1076
+ const input = event.currentTarget;
1077
+ if (!(input instanceof HTMLInputElement) || this.props.disabled)
1078
+ return;
1079
+ const value = input.checked;
1080
+ if (this.fieldContext)
1081
+ this.fieldContext.model.setValue(this.fieldContext.name, value);
1082
+ else if (this.props.checked === undefined)
1083
+ this.state.internalChecked = value;
1084
+ this.emit(eventName, {
1085
+ value,
1086
+ originalEvent: event
1087
+ });
1088
+ if (!this.fieldContext && this.props.checked !== undefined)
1089
+ input.checked = this.props.checked;
1090
+ }
1091
+ }
1092
+ // lib/select/OneSelect.ts
1093
+ import { Component as Component8 } from "@geektech/tsone";
1094
+ var ONE_SELECT_STYLES = [
1095
+ {
1096
+ name: "one-select-base",
1097
+ selector: ".one-select",
1098
+ properties: { position: "relative", width: "100%" }
1099
+ },
1100
+ {
1101
+ name: "one-select-trigger",
1102
+ selector: ".one-select__trigger",
1103
+ properties: {
1104
+ width: "100%",
1105
+ minHeight: "40px",
1106
+ textAlign: "left",
1107
+ border: `1px solid var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder})`,
1108
+ borderRadius: `var(--one-radius-md, ${ONE_THEME_DEFAULTS.radiusMd})`,
1109
+ backgroundColor: `var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface})`
1110
+ }
1111
+ },
1112
+ {
1113
+ name: "one-select-menu",
1114
+ selector: ".one-select__menu",
1115
+ properties: {
1116
+ position: "absolute",
1117
+ zIndex: "1",
1118
+ width: "100%",
1119
+ marginTop: "4px",
1120
+ padding: "4px",
1121
+ border: `1px solid var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder})`,
1122
+ borderRadius: `var(--one-radius-md, ${ONE_THEME_DEFAULTS.radiusMd})`,
1123
+ backgroundColor: `var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface})`
1124
+ }
1125
+ },
1126
+ {
1127
+ name: "one-select-option",
1128
+ selector: ".one-select__option",
1129
+ properties: {
1130
+ display: "block",
1131
+ width: "100%",
1132
+ padding: "8px",
1133
+ textAlign: "left",
1134
+ backgroundColor: "transparent",
1135
+ border: "0"
1136
+ }
1137
+ },
1138
+ {
1139
+ name: "one-select-option-selected",
1140
+ selector: ".one-select__option--selected",
1141
+ properties: {
1142
+ color: `var(--one-color-primary, ${ONE_THEME_DEFAULTS.colorPrimary})`
1143
+ }
1144
+ },
1145
+ {
1146
+ name: "one-select-option-disabled",
1147
+ selector: ".one-select__option--disabled",
1148
+ properties: { opacity: "0.5", cursor: "not-allowed" }
1149
+ }
1150
+ ];
1151
+ function isGroup(option) {
1152
+ return "options" in option;
1153
+ }
1154
+ function flattenOptions(options) {
1155
+ return options.flatMap((option) => isGroup(option) ? option.options.map((item) => ({ ...item, group: option.label })) : [option]);
1156
+ }
1157
+ function normalizeSelectValue(value, multiple) {
1158
+ if (multiple)
1159
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? [...value] : [];
1160
+ return typeof value === "string" ? value : "";
1161
+ }
1162
+ function matchesSearch(option, query) {
1163
+ return option.label.toLowerCase().includes(query.trim().toLowerCase());
1164
+ }
1165
+
1166
+ class OneSelect extends Component8 {
1167
+ fieldContext;
1168
+ unsubscribe;
1169
+ initState() {
1170
+ return {
1171
+ open: false,
1172
+ query: "",
1173
+ activeIndex: 0,
1174
+ internalValue: normalizeSelectValue(this.props.defaultValue, this.props.multiple === true),
1175
+ revision: 0
1176
+ };
1177
+ }
1178
+ initStyles() {
1179
+ ONE_SELECT_STYLES.forEach(({ name, ...style }) => this.styleManager.addStyle(name, style));
1180
+ }
1181
+ beforeMount() {
1182
+ this.fieldContext = this.inject(ONE_FORM_FIELD_KEY);
1183
+ this.fieldContext?.model.ensureValue(this.fieldContext.name, normalizeSelectValue(this.props.value ?? this.props.defaultValue, this.props.multiple === true));
1184
+ }
1185
+ onMounted() {
1186
+ this.unsubscribe = this.fieldContext?.model.subscribe(() => {
1187
+ this.state.revision += 1;
1188
+ });
1189
+ }
1190
+ onUnmounted() {
1191
+ this.unsubscribe?.();
1192
+ this.unsubscribe = undefined;
1193
+ this.fieldContext = undefined;
1194
+ }
1195
+ render() {
1196
+ this.state.revision;
1197
+ const multiple = this.props.multiple === true;
1198
+ const values = this.getValue();
1199
+ const selected = new Set(Array.isArray(values) ? values : values ? [values] : []);
1200
+ const all = flattenOptions(this.props.options);
1201
+ const visible = all.filter((option) => matchesSearch(option, this.state.query));
1202
+ const invalid = this.props.invalid || (this.fieldContext?.model.getErrors(this.fieldContext.name).length ?? 0) > 0;
1203
+ const labels = all.filter((option) => selected.has(option.value)).map((option) => option.label);
1204
+ return {
1205
+ tag: "div",
1206
+ props: { className: "one-select" },
1207
+ children: [
1208
+ {
1209
+ tag: "button",
1210
+ props: {
1211
+ type: "button",
1212
+ className: "one-select__trigger",
1213
+ role: "combobox",
1214
+ disabled: this.props.disabled === true,
1215
+ id: this.fieldContext?.controlId,
1216
+ name: this.fieldContext?.name ?? this.props.name,
1217
+ "aria-label": this.props.ariaLabel,
1218
+ "aria-expanded": this.state.open ? "true" : "false",
1219
+ "aria-controls": "one-select-listbox",
1220
+ "aria-invalid": invalid ? "true" : undefined,
1221
+ "aria-describedby": this.fieldContext?.describedBy
1222
+ },
1223
+ children: [labels.join(", ") || this.props.placeholder || "请选择"],
1224
+ listeners: {
1225
+ click: () => {
1226
+ if (!this.props.disabled)
1227
+ this.state.open = !this.state.open;
1228
+ },
1229
+ keydown: (event) => this.handleKeydown(event, visible)
1230
+ }
1231
+ },
1232
+ ...this.state.open ? [
1233
+ {
1234
+ tag: "div",
1235
+ props: {
1236
+ className: "one-select__menu",
1237
+ id: "one-select-listbox",
1238
+ role: "listbox",
1239
+ "aria-multiselectable": multiple ? "true" : undefined
1240
+ },
1241
+ children: [
1242
+ ...this.props.searchable ? [
1243
+ {
1244
+ tag: "input",
1245
+ props: {
1246
+ type: "search",
1247
+ value: this.state.query,
1248
+ "aria-label": "搜索选项"
1249
+ },
1250
+ listeners: {
1251
+ input: (event) => {
1252
+ const input = event.currentTarget;
1253
+ if (input instanceof HTMLInputElement)
1254
+ this.state.query = input.value;
1255
+ }
1256
+ }
1257
+ }
1258
+ ] : [],
1259
+ ...visible.flatMap((option, index) => [
1260
+ ...option.group && (index === 0 || visible[index - 1].group !== option.group) ? [
1261
+ {
1262
+ tag: "div",
1263
+ props: { className: "one-select__group" },
1264
+ children: [option.group]
1265
+ }
1266
+ ] : [],
1267
+ {
1268
+ tag: "button",
1269
+ props: {
1270
+ type: "button",
1271
+ className: [
1272
+ "one-select__option",
1273
+ ...selected.has(option.value) ? ["one-select__option--selected"] : [],
1274
+ ...option.disabled ? ["one-select__option--disabled"] : []
1275
+ ].join(" "),
1276
+ role: "option",
1277
+ disabled: option.disabled === true,
1278
+ "aria-selected": selected.has(option.value) ? "true" : "false"
1279
+ },
1280
+ children: [option.label],
1281
+ listeners: {
1282
+ click: (event) => this.selectOption(option, event)
1283
+ }
1284
+ }
1285
+ ])
1286
+ ]
1287
+ }
1288
+ ] : []
1289
+ ]
1290
+ };
1291
+ }
1292
+ getValue() {
1293
+ const value = this.fieldContext?.model.getValue(this.fieldContext.name);
1294
+ return value !== undefined ? normalizeSelectValue(value, this.props.multiple === true) : normalizeSelectValue(this.props.value ?? this.state.internalValue, this.props.multiple === true);
1295
+ }
1296
+ selectOption(option, event) {
1297
+ if (option.disabled)
1298
+ return;
1299
+ const multiple = this.props.multiple === true;
1300
+ const current = this.getValue();
1301
+ const next = multiple ? this.normalizeMultiple([
1302
+ ...current,
1303
+ ...current.includes(option.value) ? [] : [option.value]
1304
+ ], option.value) : option.value;
1305
+ if (multiple && current.includes(option.value)) {
1306
+ const values = current.filter((value) => value !== option.value);
1307
+ this.commit(values, event);
1308
+ return;
1309
+ }
1310
+ this.commit(next, event);
1311
+ if (!multiple)
1312
+ this.state.open = false;
1313
+ }
1314
+ normalizeMultiple(values, _changed) {
1315
+ const selected = new Set(values);
1316
+ return flattenOptions(this.props.options).filter((option) => selected.has(option.value)).map((option) => option.value);
1317
+ }
1318
+ commit(value, event) {
1319
+ if (this.fieldContext)
1320
+ this.fieldContext.model.setValue(this.fieldContext.name, value);
1321
+ else if (this.props.value === undefined)
1322
+ this.state.internalValue = value;
1323
+ this.emit("input", {
1324
+ value,
1325
+ originalEvent: event
1326
+ });
1327
+ this.emit("change", {
1328
+ value,
1329
+ originalEvent: event
1330
+ });
1331
+ }
1332
+ handleKeydown(event, options) {
1333
+ if (!(event instanceof KeyboardEvent))
1334
+ return;
1335
+ if (event.key === "Escape") {
1336
+ this.state.open = false;
1337
+ return;
1338
+ }
1339
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1340
+ event.preventDefault();
1341
+ this.state.open = true;
1342
+ const direction = event.key === "ArrowDown" ? 1 : -1;
1343
+ this.state.activeIndex = (this.state.activeIndex + direction + options.length) % options.length;
1344
+ return;
1345
+ }
1346
+ if (event.key === "Enter" && this.state.open && options[this.state.activeIndex]) {
1347
+ event.preventDefault();
1348
+ this.selectOption(options[this.state.activeIndex], event);
1349
+ }
1350
+ }
1351
+ }
1352
+ // lib/categories.ts
1353
+ var ONE_COMPONENT_CATEGORIES = [
1354
+ { id: "basic", label: "基础", components: ["OneButton", "OneInput"] },
1355
+ {
1356
+ id: "form",
1357
+ label: "表单",
1358
+ components: [
1359
+ "OneForm",
1360
+ "OneFormItem",
1361
+ "OneSelect",
1362
+ "OneCheckbox",
1363
+ "OneCheckboxGroup",
1364
+ "OneSwitch"
1365
+ ]
1366
+ },
1367
+ { id: "data-display", label: "数据展示", components: ["OneCard"] },
1368
+ { id: "feedback", label: "反馈与浮层", components: [] }
1369
+ ];
1370
+ // lib/card/OneCard.ts
1371
+ import { Component as Component9, slot as slot4 } from "@geektech/tsone";
1372
+ var ONE_CARD_STYLES = [
1373
+ {
1374
+ name: "one-card-base",
1375
+ selector: ".one-card",
1376
+ properties: {
1377
+ overflow: "hidden",
1378
+ color: `var(--one-card-color, var(--one-color-text, ${ONE_THEME_DEFAULTS.colorText}))`,
1379
+ backgroundColor: `var(--one-card-background, var(--one-color-surface, ${ONE_THEME_DEFAULTS.colorSurface}))`,
1380
+ border: `1px solid var(--one-card-border-color, var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder}))`,
1381
+ borderRadius: `var(--one-card-radius, var(--one-radius-md, ${ONE_THEME_DEFAULTS.radiusMd}))`,
1382
+ boxShadow: `var(--one-card-shadow, var(--one-shadow-card, ${ONE_THEME_DEFAULTS.shadowCard}))`
1383
+ }
1384
+ },
1385
+ {
1386
+ name: "one-card-header",
1387
+ selector: ".one-card__header",
1388
+ properties: {
1389
+ padding: `var(--one-card-header-padding, var(--one-space-lg, ${ONE_THEME_DEFAULTS.spaceLg}))`,
1390
+ borderBottom: `1px solid var(--one-card-border-color, var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder}))`,
1391
+ fontWeight: "500"
1392
+ }
1393
+ },
1394
+ {
1395
+ name: "one-card-body",
1396
+ selector: ".one-card__body",
1397
+ properties: {
1398
+ padding: `var(--one-card-body-padding, var(--one-space-lg, ${ONE_THEME_DEFAULTS.spaceLg}))`
1399
+ }
1400
+ },
1401
+ {
1402
+ name: "one-card-footer",
1403
+ selector: ".one-card__footer",
1404
+ properties: {
1405
+ padding: `var(--one-card-footer-padding, var(--one-space-lg, ${ONE_THEME_DEFAULTS.spaceLg}))`,
1406
+ borderTop: `1px solid var(--one-card-border-color, var(--one-color-border, ${ONE_THEME_DEFAULTS.colorBorder}))`
1407
+ }
1408
+ }
1409
+ ];
1410
+ function hasNamedSlot(children, name) {
1411
+ return children.some((child) => typeof child !== "string" && child.slot === name);
1412
+ }
1413
+
1414
+ class OneCard extends Component9 {
1415
+ initState() {
1416
+ return {};
1417
+ }
1418
+ initStyles() {
1419
+ ONE_CARD_STYLES.forEach(({ name, ...style }) => {
1420
+ this.styleManager.addStyle(name, style);
1421
+ });
1422
+ }
1423
+ render() {
1424
+ const children = this.props.children ?? [];
1425
+ const hasHeaderSlot = hasNamedSlot(children, "header");
1426
+ const hasFooterSlot = hasNamedSlot(children, "footer");
1427
+ const headerChildren = hasHeaderSlot ? [slot4("header")] : this.props.title ? [this.props.title] : [];
1428
+ return {
1429
+ tag: "section",
1430
+ props: { className: "one-card" },
1431
+ children: [
1432
+ ...headerChildren.length > 0 ? [
1433
+ {
1434
+ tag: "header",
1435
+ props: { className: "one-card__header" },
1436
+ children: headerChildren
1437
+ }
1438
+ ] : [],
1439
+ {
1440
+ tag: "div",
1441
+ props: { className: "one-card__body" },
1442
+ children: [slot4("default")]
1443
+ },
1444
+ ...hasFooterSlot ? [
1445
+ {
1446
+ tag: "footer",
1447
+ props: { className: "one-card__footer" },
1448
+ children: [slot4("footer")]
1449
+ }
1450
+ ] : []
1451
+ ]
1452
+ };
1453
+ }
1454
+ }
1455
+ // lib/index.ts
1456
+ var ONE_NAME = "@geektech/one";
1457
+ var ONE_VERSION = "0.0.1";
1458
+ export {
1459
+ OneSwitch,
1460
+ OneSelect,
1461
+ OneInput,
1462
+ OneFormModel,
1463
+ OneFormItem,
1464
+ OneForm,
1465
+ OneCheckboxGroup,
1466
+ OneCheckbox,
1467
+ OneCard,
1468
+ OneButton,
1469
+ ONE_VERSION,
1470
+ ONE_THEME_DEFAULTS,
1471
+ ONE_NAME,
1472
+ ONE_COMPONENT_CATEGORIES
1473
+ };
1474
+
1475
+ //# debugId=789C7C07D840D57764756E2164756E21
1476
+ //# sourceMappingURL=index.js.map