@konomi-app/ui 5.2.1 → 5.4.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/dist/index.cjs CHANGED
@@ -32,13 +32,24 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
32
32
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
33
33
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
34
34
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
35
+ var __privateWrapper = (obj, member, setter, getter) => ({
36
+ set _(value) {
37
+ __privateSet(obj, member, value, setter);
38
+ },
39
+ get _() {
40
+ return __privateGet(obj, member, getter);
41
+ }
42
+ });
35
43
 
36
44
  // src/index.ts
37
45
  var index_exports = {};
38
46
  __export(index_exports, {
39
47
  DialogController: () => DialogController,
40
48
  OverlayDialog: () => OverlayDialog,
41
- dialog: () => dialog
49
+ ToastContainer: () => ToastContainer,
50
+ ToastController: () => ToastController,
51
+ dialog: () => dialog,
52
+ toast: () => toast
42
53
  });
43
54
  module.exports = __toCommonJS(index_exports);
44
55
 
@@ -60,14 +71,168 @@ var createInitialState = () => ({
60
71
  queues: [],
61
72
  steps: [],
62
73
  timer: null,
63
- title: ""
74
+ title: "",
75
+ formFields: [],
76
+ formValues: {},
77
+ formErrors: {},
78
+ formTouched: {},
79
+ formLayout: {},
80
+ formValidateOnChange: true,
81
+ formValidateOnBlur: true,
82
+ stepFormSteps: [],
83
+ stepFormCurrentIndex: 0,
84
+ stepFormNextText: "\u6B21\u3078",
85
+ stepFormPrevText: "\u623B\u308B",
86
+ stepFormSubmitText: "OK"
64
87
  });
65
88
 
89
+ // src/zod-utils.ts
90
+ function resolveTypeName(field) {
91
+ const raw = field._def.typeName ?? field._def.type;
92
+ if (!raw) return "";
93
+ const v4Map = {
94
+ string: "ZodString",
95
+ number: "ZodNumber",
96
+ boolean: "ZodBoolean",
97
+ enum: "ZodEnum",
98
+ date: "ZodDate",
99
+ optional: "ZodOptional",
100
+ nullable: "ZodNullable",
101
+ default: "ZodDefault",
102
+ object: "ZodObject",
103
+ pipe: "ZodPipe"
104
+ };
105
+ return v4Map[raw] ?? raw;
106
+ }
107
+ function unwrapType(zodType) {
108
+ let inner = zodType;
109
+ let required = true;
110
+ let defaultValue = void 0;
111
+ let description = zodType.description ?? "";
112
+ while (true) {
113
+ const typeName = resolveTypeName(inner);
114
+ if (typeName === "ZodOptional" || typeName === "ZodNullable") {
115
+ required = false;
116
+ inner = inner._def.innerType;
117
+ } else if (typeName === "ZodDefault") {
118
+ const raw = inner._def.defaultValue;
119
+ defaultValue = typeof raw === "function" ? raw() : raw;
120
+ inner = inner._def.innerType;
121
+ } else if (typeName === "ZodEffects") {
122
+ inner = inner._def.schema;
123
+ } else {
124
+ break;
125
+ }
126
+ if (!description && inner.description) {
127
+ description = inner.description;
128
+ }
129
+ }
130
+ return { inner, required, defaultValue, description };
131
+ }
132
+ function extractFieldMeta(key, zodType) {
133
+ const { inner, required, defaultValue, description } = unwrapType(zodType);
134
+ const typeName = resolveTypeName(inner);
135
+ let inputType;
136
+ let options = [];
137
+ let min;
138
+ let max;
139
+ let minLength;
140
+ let maxLength;
141
+ switch (typeName) {
142
+ case "ZodString": {
143
+ inputType = "text";
144
+ if (inner.format === "email") inputType = "email";
145
+ else if (inner.format === "url") inputType = "url";
146
+ if (inner.minLength != null) minLength = inner.minLength;
147
+ if (inner.maxLength != null) maxLength = inner.maxLength;
148
+ const checks = inner._def.checks ?? [];
149
+ for (const check of checks) {
150
+ const kind = check.kind ?? check.def?.check;
151
+ const fmt = check.format ?? check.def?.format;
152
+ if (!fmt && (kind === "email" || fmt === "email")) inputType = "email";
153
+ else if (!fmt && (kind === "url" || fmt === "url")) inputType = "url";
154
+ else if (fmt === "email" && inputType === "text") inputType = "email";
155
+ else if (fmt === "url" && inputType === "text") inputType = "url";
156
+ if (kind === "min" && check.value != null && minLength == null) minLength = check.value;
157
+ if (kind === "max" && check.value != null && maxLength == null) maxLength = check.value;
158
+ }
159
+ break;
160
+ }
161
+ case "ZodNumber": {
162
+ inputType = "number";
163
+ if (inner.minValue != null) min = inner.minValue;
164
+ if (inner.maxValue != null) max = inner.maxValue;
165
+ if (min == null || max == null) {
166
+ const checks = inner._def.checks ?? [];
167
+ for (const check of checks) {
168
+ if (check.kind === "min" && check.value != null && min == null) min = check.value;
169
+ if (check.kind === "max" && check.value != null && max == null) max = check.value;
170
+ }
171
+ }
172
+ break;
173
+ }
174
+ case "ZodBoolean":
175
+ inputType = "checkbox";
176
+ break;
177
+ case "ZodEnum": {
178
+ inputType = "select";
179
+ if (inner.options?.length) {
180
+ options = [...inner.options];
181
+ } else if (inner._def.entries) {
182
+ options = Object.values(inner._def.entries);
183
+ } else if (inner._def.values?.length) {
184
+ options = [...inner._def.values];
185
+ }
186
+ break;
187
+ }
188
+ case "ZodNativeEnum": {
189
+ inputType = "select";
190
+ const enumValues = inner._def.values;
191
+ if (enumValues) {
192
+ options = Object.values(enumValues).filter((v) => typeof v === "string");
193
+ }
194
+ break;
195
+ }
196
+ case "ZodDate":
197
+ inputType = "date";
198
+ break;
199
+ default:
200
+ return null;
201
+ }
202
+ const label = description || key;
203
+ return {
204
+ key,
205
+ inputType,
206
+ label,
207
+ description: description && description !== label ? description : "",
208
+ required,
209
+ options,
210
+ placeholder: "",
211
+ min,
212
+ max,
213
+ minLength,
214
+ maxLength,
215
+ defaultValue
216
+ };
217
+ }
218
+ function extractFormFields(schema) {
219
+ const shapeDef = schema._def.shape;
220
+ if (!shapeDef) return [];
221
+ const shape = typeof shapeDef === "function" ? shapeDef() : shapeDef;
222
+ if (!shape) return [];
223
+ const fields = [];
224
+ for (const [key, zodType] of Object.entries(shape)) {
225
+ const meta = extractFieldMeta(key, zodType);
226
+ if (meta) fields.push(meta);
227
+ }
228
+ return fields;
229
+ }
230
+
66
231
  // src/controller.ts
67
232
  function normalizeItemInput(input) {
68
233
  return typeof input === "string" ? { key: input, label: input } : input;
69
234
  }
70
- var _state, _listeners, _resolver, _timerId, _DialogController_instances, emit_fn, update_fn, createPromise_fn, resolve_fn, clearTimer_fn, updateItemStatus_fn;
235
+ var _state, _listeners, _resolver, _timerId, _formSchema, _formResult, _stepFormSchemas, _stepFormResults, _DialogController_instances, emit_fn, update_fn, validateFormField_fn, updateCurrentStep_fn, validateStepField_fn, createPromise_fn, resolve_fn, clearTimer_fn, updateItemStatus_fn;
71
236
  var DialogController = class {
72
237
  constructor() {
73
238
  __privateAdd(this, _DialogController_instances);
@@ -75,6 +240,10 @@ var DialogController = class {
75
240
  __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
76
241
  __privateAdd(this, _resolver, null);
77
242
  __privateAdd(this, _timerId, null);
243
+ __privateAdd(this, _formSchema, null);
244
+ __privateAdd(this, _formResult, null);
245
+ __privateAdd(this, _stepFormSchemas, []);
246
+ __privateAdd(this, _stepFormResults, {});
78
247
  __privateSet(this, _state, createInitialState());
79
248
  }
80
249
  // ─── Observable ──────────────────────────────────────────
@@ -220,9 +389,202 @@ var DialogController = class {
220
389
  clearSteps() {
221
390
  __privateMethod(this, _DialogController_instances, update_fn).call(this, { steps: [] });
222
391
  }
392
+ // ─── Form ─────────────────────────────────────────────────
393
+ form(schema, options) {
394
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
395
+ __privateSet(this, _formSchema, schema);
396
+ __privateSet(this, _formResult, null);
397
+ const fields = extractFormFields(schema);
398
+ const defaultValues = {};
399
+ for (const field of fields) {
400
+ if (field.defaultValue !== void 0) {
401
+ defaultValues[field.key] = field.defaultValue;
402
+ }
403
+ }
404
+ if (options?.defaultValues) {
405
+ Object.assign(defaultValues, options.defaultValues);
406
+ }
407
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
408
+ open: true,
409
+ dialogType: "form",
410
+ title: options?.title ?? "",
411
+ label: "",
412
+ description: options?.description ?? "",
413
+ icon: null,
414
+ showConfirmButton: true,
415
+ showCancelButton: true,
416
+ confirmButtonText: options?.confirmButtonText ?? "OK",
417
+ cancelButtonText: options?.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB",
418
+ allowOutsideClick: options?.allowOutsideClick ?? false,
419
+ allowEscapeKey: options?.allowEscapeKey ?? true,
420
+ progress: null,
421
+ timer: null,
422
+ formFields: fields,
423
+ formValues: defaultValues,
424
+ formErrors: {},
425
+ formTouched: {},
426
+ formLayout: options?.layout ?? {},
427
+ formValidateOnChange: options?.validateOnChange ?? true,
428
+ formValidateOnBlur: options?.validateOnBlur ?? true
429
+ });
430
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, null).then((r) => {
431
+ const data = __privateGet(this, _formResult);
432
+ __privateSet(this, _formSchema, null);
433
+ __privateSet(this, _formResult, null);
434
+ return r.isConfirmed ? data : null;
435
+ });
436
+ }
437
+ updateFormField(key, value) {
438
+ const formValues = { ...__privateGet(this, _state).formValues, [key]: value };
439
+ const formTouched = { ...__privateGet(this, _state).formTouched, [key]: true };
440
+ let formErrors = { ...__privateGet(this, _state).formErrors };
441
+ if (__privateGet(this, _formSchema) && __privateGet(this, _state).formValidateOnChange) {
442
+ formErrors = __privateMethod(this, _DialogController_instances, validateFormField_fn).call(this, key, formValues, formErrors);
443
+ }
444
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formValues, formTouched, formErrors });
445
+ }
446
+ touchFormField(key) {
447
+ const formTouched = { ...__privateGet(this, _state).formTouched, [key]: true };
448
+ let formErrors = { ...__privateGet(this, _state).formErrors };
449
+ if (__privateGet(this, _formSchema) && __privateGet(this, _state).formValidateOnBlur) {
450
+ formErrors = __privateMethod(this, _DialogController_instances, validateFormField_fn).call(this, key, __privateGet(this, _state).formValues, formErrors);
451
+ }
452
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formTouched, formErrors });
453
+ }
454
+ // ─── Step Form ───────────────────────────────────────────
455
+ showStepForm(steps, options) {
456
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
457
+ __privateSet(this, _stepFormSchemas, steps.map((s) => s.schema ?? null));
458
+ __privateSet(this, _stepFormResults, {});
459
+ const stepFormSteps = steps.map((s) => {
460
+ const fields = s.schema ? extractFormFields(s.schema) : [];
461
+ const values = {};
462
+ for (const f of fields) {
463
+ if (f.defaultValue !== void 0) values[f.key] = f.defaultValue;
464
+ }
465
+ if (s.defaultValues) Object.assign(values, s.defaultValues);
466
+ return {
467
+ key: s.key,
468
+ label: s.label,
469
+ description: s.description ?? "",
470
+ fields,
471
+ values,
472
+ errors: {},
473
+ touched: {},
474
+ layout: s.layout ?? {}
475
+ };
476
+ });
477
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
478
+ open: true,
479
+ dialogType: "step-form",
480
+ title: options?.title ?? "",
481
+ label: "",
482
+ description: "",
483
+ icon: null,
484
+ showConfirmButton: false,
485
+ showCancelButton: false,
486
+ allowOutsideClick: options?.allowOutsideClick ?? false,
487
+ allowEscapeKey: options?.allowEscapeKey ?? true,
488
+ progress: null,
489
+ timer: null,
490
+ stepFormSteps,
491
+ stepFormCurrentIndex: 0,
492
+ stepFormNextText: options?.nextButtonText ?? "\u6B21\u3078",
493
+ stepFormPrevText: options?.prevButtonText ?? "\u623B\u308B",
494
+ stepFormSubmitText: options?.submitButtonText ?? "OK",
495
+ cancelButtonText: options?.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB"
496
+ });
497
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, null).then((r) => {
498
+ const data = __privateGet(this, _stepFormResults);
499
+ __privateSet(this, _stepFormSchemas, []);
500
+ __privateSet(this, _stepFormResults, {});
501
+ return r.isConfirmed ? data : null;
502
+ });
503
+ }
504
+ onStepNext() {
505
+ const s = __privateGet(this, _state);
506
+ const idx = s.stepFormCurrentIndex;
507
+ const step = s.stepFormSteps[idx];
508
+ if (!step) return;
509
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
510
+ if (schema) {
511
+ const result = schema.safeParse(step.values);
512
+ if (!result.success) {
513
+ const errors = {};
514
+ for (const issue of result.error.issues) {
515
+ const key = issue.path[0]?.toString();
516
+ if (key && !errors[key]) errors[key] = issue.message;
517
+ }
518
+ const touched = {};
519
+ for (const f of step.fields) touched[f.key] = true;
520
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { errors, touched });
521
+ return;
522
+ }
523
+ __privateGet(this, _stepFormResults)[step.key] = result.data;
524
+ }
525
+ const isLast = idx === s.stepFormSteps.length - 1;
526
+ if (isLast) {
527
+ const r = { isConfirmed: true, isCanceled: false, isDismissed: false };
528
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
529
+ __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
530
+ return;
531
+ }
532
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormCurrentIndex: idx + 1 });
533
+ }
534
+ onStepPrev() {
535
+ const idx = __privateGet(this, _state).stepFormCurrentIndex;
536
+ if (idx <= 0) return;
537
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormCurrentIndex: idx - 1 });
538
+ }
539
+ updateStepFormField(fieldKey, value) {
540
+ const s = __privateGet(this, _state);
541
+ const idx = s.stepFormCurrentIndex;
542
+ const step = s.stepFormSteps[idx];
543
+ if (!step) return;
544
+ const values = { ...step.values, [fieldKey]: value };
545
+ const touched = { ...step.touched, [fieldKey]: true };
546
+ let errors = { ...step.errors };
547
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
548
+ if (schema && s.formValidateOnChange) {
549
+ errors = __privateMethod(this, _DialogController_instances, validateStepField_fn).call(this, schema, fieldKey, values, errors);
550
+ }
551
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { values, touched, errors });
552
+ }
553
+ touchStepFormField(fieldKey) {
554
+ const s = __privateGet(this, _state);
555
+ const idx = s.stepFormCurrentIndex;
556
+ const step = s.stepFormSteps[idx];
557
+ if (!step) return;
558
+ const touched = { ...step.touched, [fieldKey]: true };
559
+ let errors = { ...step.errors };
560
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
561
+ if (schema && s.formValidateOnBlur) {
562
+ errors = __privateMethod(this, _DialogController_instances, validateStepField_fn).call(this, schema, fieldKey, step.values, errors);
563
+ }
564
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { touched, errors });
565
+ }
223
566
  // ─── Button actions (called from the component) ──────────
224
567
  onConfirm() {
225
568
  __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
569
+ if (__privateGet(this, _state).dialogType === "form" && __privateGet(this, _formSchema)) {
570
+ const result = __privateGet(this, _formSchema).safeParse(__privateGet(this, _state).formValues);
571
+ if (!result.success) {
572
+ const formErrors = {};
573
+ for (const issue of result.error.issues) {
574
+ const key = issue.path[0]?.toString();
575
+ if (key && !formErrors[key]) {
576
+ formErrors[key] = issue.message;
577
+ }
578
+ }
579
+ const formTouched = {};
580
+ for (const field of __privateGet(this, _state).formFields) {
581
+ formTouched[field.key] = true;
582
+ }
583
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formErrors, formTouched });
584
+ return;
585
+ }
586
+ __privateSet(this, _formResult, result.data);
587
+ }
226
588
  const r = { isConfirmed: true, isCanceled: false, isDismissed: false };
227
589
  __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
228
590
  __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
@@ -246,6 +608,10 @@ _state = new WeakMap();
246
608
  _listeners = new WeakMap();
247
609
  _resolver = new WeakMap();
248
610
  _timerId = new WeakMap();
611
+ _formSchema = new WeakMap();
612
+ _formResult = new WeakMap();
613
+ _stepFormSchemas = new WeakMap();
614
+ _stepFormResults = new WeakMap();
249
615
  _DialogController_instances = new WeakSet();
250
616
  emit_fn = function() {
251
617
  const snapshot = { ...__privateGet(this, _state) };
@@ -255,6 +621,47 @@ update_fn = function(patch) {
255
621
  Object.assign(__privateGet(this, _state), patch);
256
622
  __privateMethod(this, _DialogController_instances, emit_fn).call(this);
257
623
  };
624
+ validateFormField_fn = function(key, values, errors) {
625
+ const result = __privateGet(this, _formSchema).safeParse(values);
626
+ const updated = { ...errors };
627
+ if (result.success) {
628
+ delete updated[key];
629
+ } else {
630
+ const fieldIssue = result.error.issues.find(
631
+ (issue) => issue.path[0]?.toString() === key
632
+ );
633
+ if (fieldIssue) {
634
+ updated[key] = fieldIssue.message;
635
+ } else {
636
+ delete updated[key];
637
+ }
638
+ }
639
+ return updated;
640
+ };
641
+ updateCurrentStep_fn = function(patch) {
642
+ const idx = __privateGet(this, _state).stepFormCurrentIndex;
643
+ const stepFormSteps = __privateGet(this, _state).stepFormSteps.map(
644
+ (st, i) => i === idx ? { ...st, ...patch } : st
645
+ );
646
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormSteps });
647
+ };
648
+ validateStepField_fn = function(schema, fieldKey, values, errors) {
649
+ const result = schema.safeParse(values);
650
+ const updated = { ...errors };
651
+ if (result.success) {
652
+ delete updated[fieldKey];
653
+ } else {
654
+ const issue = result.error.issues.find(
655
+ (iss) => iss.path[0]?.toString() === fieldKey
656
+ );
657
+ if (issue) {
658
+ updated[fieldKey] = issue.message;
659
+ } else {
660
+ delete updated[fieldKey];
661
+ }
662
+ }
663
+ return updated;
664
+ };
258
665
  // ─── Internal ────────────────────────────────────────────
259
666
  createPromise_fn = function(timer) {
260
667
  return new Promise((resolve) => {
@@ -343,6 +750,24 @@ var overlayStyles = import_lit.css`
343
750
  --dialog-spinner-track: rgb(59 130 246 / 0.2);
344
751
  --dialog-spinner-arc: var(--dialog-primary);
345
752
 
753
+ /* Form */
754
+ --dialog-form-width: 500px;
755
+ --dialog-form-max-height: 60vh;
756
+ --dialog-form-gap: 16px;
757
+ --dialog-form-columns: 1;
758
+ --dialog-form-label-color: #374151;
759
+ --dialog-form-label-size: 13px;
760
+ --dialog-form-label-weight: 500;
761
+ --dialog-form-input-bg: #fff;
762
+ --dialog-form-input-border: #d1d5db;
763
+ --dialog-form-input-border-focus: var(--dialog-primary);
764
+ --dialog-form-input-radius: 6px;
765
+ --dialog-form-input-padding: 8px 12px;
766
+ --dialog-form-input-font-size: 14px;
767
+ --dialog-form-error-color: var(--dialog-error);
768
+ --dialog-form-hint-color: #9ca3af;
769
+ --dialog-form-required-color: var(--dialog-error);
770
+
346
771
  display: contents;
347
772
  font-family: var(--dialog-font-family);
348
773
  color: var(--dialog-text-color);
@@ -494,8 +919,6 @@ var overlayStyles = import_lit.css`
494
919
  }
495
920
  }
496
921
 
497
- /* spin と spinner-enter は同じ transform を書き換えるため衝突する。
498
- ラッパーでスケール/フェードを担い、.spinner は回転専用にする。 */
499
922
  .spinner-wrap {
500
923
  font-size: var(--dialog-spinner-size);
501
924
  width: 1em;
@@ -978,6 +1401,204 @@ var overlayStyles = import_lit.css`
978
1401
  background-color: #e5e7eb;
979
1402
  transition: background-color 400ms ease;
980
1403
  }
1404
+
1405
+ /* ─── Form ─── */
1406
+
1407
+ @media (min-width: 640px) {
1408
+ .card[data-type='form'] {
1409
+ width: var(--dialog-form-width);
1410
+ }
1411
+ }
1412
+
1413
+ .form-scroll-container {
1414
+ max-height: var(--dialog-form-max-height);
1415
+ overflow-y: auto;
1416
+ width: 100%;
1417
+ padding: 4px 0;
1418
+ }
1419
+
1420
+ .form-grid {
1421
+ display: grid;
1422
+ grid-template-columns: repeat(var(--dialog-form-columns, 1), 1fr);
1423
+ gap: var(--dialog-form-gap);
1424
+ width: 100%;
1425
+ }
1426
+
1427
+ @media (max-width: 639px) {
1428
+ .form-grid {
1429
+ grid-template-columns: 1fr !important;
1430
+ }
1431
+ }
1432
+
1433
+ .form-field {
1434
+ display: flex;
1435
+ flex-direction: column;
1436
+ gap: 4px;
1437
+ text-align: left;
1438
+ }
1439
+
1440
+ .form-field[data-type='checkbox'] {
1441
+ grid-column: 1 / -1;
1442
+ }
1443
+
1444
+ .form-label {
1445
+ font-size: var(--dialog-form-label-size);
1446
+ font-weight: var(--dialog-form-label-weight);
1447
+ color: var(--dialog-form-label-color);
1448
+ }
1449
+
1450
+ .form-required {
1451
+ color: var(--dialog-form-required-color);
1452
+ margin-left: 2px;
1453
+ }
1454
+
1455
+ .form-input,
1456
+ .form-select {
1457
+ padding: var(--dialog-form-input-padding);
1458
+ font-size: var(--dialog-form-input-font-size);
1459
+ font-family: inherit;
1460
+ background: var(--dialog-form-input-bg);
1461
+ border: 1px solid var(--dialog-form-input-border);
1462
+ border-radius: var(--dialog-form-input-radius);
1463
+ color: var(--dialog-text-color);
1464
+ outline: none;
1465
+ transition:
1466
+ border-color 150ms ease,
1467
+ box-shadow 150ms ease;
1468
+ width: 100%;
1469
+ box-sizing: border-box;
1470
+ }
1471
+
1472
+ .form-input:focus,
1473
+ .form-select:focus {
1474
+ border-color: var(--dialog-form-input-border-focus);
1475
+ box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
1476
+ }
1477
+
1478
+ .form-field[data-error] .form-input,
1479
+ .form-field[data-error] .form-select {
1480
+ border-color: var(--dialog-form-error-color);
1481
+ }
1482
+
1483
+ .form-field[data-error] .form-input:focus,
1484
+ .form-field[data-error] .form-select:focus {
1485
+ box-shadow: 0 0 0 3px rgb(239 68 68 / 0.1);
1486
+ }
1487
+
1488
+ .form-error {
1489
+ font-size: 12px;
1490
+ color: var(--dialog-form-error-color);
1491
+ min-height: 1em;
1492
+ }
1493
+
1494
+ .form-hint {
1495
+ font-size: 12px;
1496
+ color: var(--dialog-form-hint-color);
1497
+ }
1498
+
1499
+ .form-checkbox {
1500
+ width: 18px;
1501
+ height: 18px;
1502
+ accent-color: var(--dialog-primary);
1503
+ cursor: pointer;
1504
+ flex-shrink: 0;
1505
+ }
1506
+
1507
+ .form-checkbox-label {
1508
+ display: flex;
1509
+ align-items: center;
1510
+ gap: 8px;
1511
+ cursor: pointer;
1512
+ font-size: var(--dialog-form-input-font-size);
1513
+ color: var(--dialog-form-label-color);
1514
+ }
1515
+
1516
+ .form-checkbox-text {
1517
+ font-size: var(--dialog-form-label-size);
1518
+ font-weight: var(--dialog-form-label-weight);
1519
+ color: var(--dialog-form-label-color);
1520
+ }
1521
+
1522
+ .form-group {
1523
+ border: 1px solid var(--dialog-card-border);
1524
+ border-radius: var(--dialog-form-input-radius);
1525
+ padding: 16px;
1526
+ margin: 0 0 8px;
1527
+ width: 100%;
1528
+ box-sizing: border-box;
1529
+ }
1530
+
1531
+ .form-group-label {
1532
+ font-size: var(--dialog-form-label-size);
1533
+ font-weight: 600;
1534
+ color: var(--dialog-form-label-color);
1535
+ padding: 0 4px;
1536
+ }
1537
+
1538
+ /* ─── Step Form ─── */
1539
+
1540
+ @media (min-width: 640px) {
1541
+ .card[data-type='step-form'] {
1542
+ width: var(--dialog-form-width);
1543
+ }
1544
+ }
1545
+
1546
+ .step-form-counter {
1547
+ font-size: 12px;
1548
+ color: var(--dialog-form-hint-color);
1549
+ text-align: center;
1550
+ margin: 0 0 4px;
1551
+ }
1552
+
1553
+ .actions-step-form {
1554
+ display: flex;
1555
+ flex-direction: row;
1556
+ justify-content: space-between;
1557
+ align-items: center;
1558
+ gap: 8px;
1559
+ margin-top: 20px;
1560
+ width: 100%;
1561
+ }
1562
+
1563
+ .step-form-nav {
1564
+ display: flex;
1565
+ flex-direction: row;
1566
+ gap: 8px;
1567
+ align-items: center;
1568
+ }
1569
+
1570
+ .btn-prev {
1571
+ padding: var(--dialog-btn-padding);
1572
+ font-size: var(--dialog-btn-font-size);
1573
+ font-family: inherit;
1574
+ font-weight: 500;
1575
+ border-radius: var(--dialog-btn-radius);
1576
+ cursor: pointer;
1577
+ border: 1px solid var(--dialog-form-input-border);
1578
+ background: transparent;
1579
+ color: var(--dialog-text-color);
1580
+ transition: background-color 120ms ease, border-color 120ms ease;
1581
+ }
1582
+
1583
+ .btn-prev:hover {
1584
+ background: #f9fafb;
1585
+ border-color: #9ca3af;
1586
+ }
1587
+
1588
+ .btn-prev:active {
1589
+ transform: scale(0.98);
1590
+ }
1591
+
1592
+ @media (max-width: 639px) {
1593
+ .actions-step-form {
1594
+ flex-direction: column-reverse;
1595
+ }
1596
+
1597
+ .step-form-nav {
1598
+ width: 100%;
1599
+ justify-content: flex-end;
1600
+ }
1601
+ }
981
1602
  `;
982
1603
 
983
1604
  // src/overlay-dialog.ts
@@ -1001,6 +1622,7 @@ var OverlayDialog = class extends import_lit2.LitElement {
1001
1622
  this._unsubscribe = this.controller.subscribe((s) => {
1002
1623
  const wasOpen = this._state.open;
1003
1624
  const prevDialogType = this._state.dialogType;
1625
+ const prevStepIndex = this._state.stepFormCurrentIndex;
1004
1626
  if (s.open && !wasOpen) {
1005
1627
  this._isClosing = false;
1006
1628
  clearTimeout(this._closeTimer);
@@ -1015,6 +1637,8 @@ var OverlayDialog = class extends import_lit2.LitElement {
1015
1637
  }
1016
1638
  if (s.open && s.dialogType !== prevDialogType) {
1017
1639
  this._bodyKey++;
1640
+ } else if (s.open && s.dialogType === "step-form" && s.stepFormCurrentIndex !== prevStepIndex) {
1641
+ this._bodyKey++;
1018
1642
  }
1019
1643
  this._state = s;
1020
1644
  this._syncBodyScroll(s.open);
@@ -1244,6 +1868,187 @@ var OverlayDialog = class extends import_lit2.LitElement {
1244
1868
  </ul>
1245
1869
  `;
1246
1870
  }
1871
+ // ─── Form Helpers ────────────────────────────────────────
1872
+ _createFormContext() {
1873
+ const s = this._state;
1874
+ return {
1875
+ getValue: (k) => s.formValues[k],
1876
+ getError: (k) => s.formErrors[k] ?? "",
1877
+ getTouched: (k) => !!s.formTouched[k],
1878
+ onUpdate: (k, v) => this.controller.updateFormField(k, v),
1879
+ onBlur: (k) => this.controller.touchFormField(k)
1880
+ };
1881
+ }
1882
+ _createStepFormContext() {
1883
+ const s = this._state;
1884
+ const step = s.stepFormSteps[s.stepFormCurrentIndex];
1885
+ return {
1886
+ getValue: (k) => step?.values[k],
1887
+ getError: (k) => step?.errors[k] ?? "",
1888
+ getTouched: (k) => !!step?.touched[k],
1889
+ onUpdate: (k, v) => this.controller.updateStepFormField(k, v),
1890
+ onBlur: (k) => this.controller.touchStepFormField(k)
1891
+ };
1892
+ }
1893
+ _getOrderedFields(fields, layout) {
1894
+ const order = layout.fieldOrder;
1895
+ if (!order?.length) return fields;
1896
+ const fieldMap = new Map(fields.map((f) => [f.key, f]));
1897
+ const ordered = [];
1898
+ for (const key of order) {
1899
+ const f = fieldMap.get(key);
1900
+ if (f) {
1901
+ ordered.push(f);
1902
+ fieldMap.delete(key);
1903
+ }
1904
+ }
1905
+ for (const f of fieldMap.values()) {
1906
+ ordered.push(f);
1907
+ }
1908
+ return ordered;
1909
+ }
1910
+ _renderFormGrid(fields, columns, gap, ctx) {
1911
+ return import_lit2.html`
1912
+ <div class="form-grid" style="--dialog-form-columns:${columns}; gap:${gap}">
1913
+ ${fields.map((f) => this._renderFormField(f, ctx))}
1914
+ </div>
1915
+ `;
1916
+ }
1917
+ _renderGroupedForm(allFields, groups, layout, ctx) {
1918
+ const fieldMap = new Map(allFields.map((f) => [f.key, f]));
1919
+ const usedKeys = /* @__PURE__ */ new Set();
1920
+ const gap = layout.gap ?? "16px";
1921
+ const groupFragments = groups.map((group) => {
1922
+ const groupFields = [];
1923
+ for (const key of group.fields) {
1924
+ const f = fieldMap.get(key);
1925
+ if (f) {
1926
+ groupFields.push(f);
1927
+ usedKeys.add(key);
1928
+ }
1929
+ }
1930
+ if (!groupFields.length) return import_lit2.nothing;
1931
+ const cols = group.columns ?? layout.columns ?? 1;
1932
+ return import_lit2.html`
1933
+ <fieldset class="form-group">
1934
+ ${group.label ? import_lit2.html`<legend class="form-group-label">${group.label}</legend>` : import_lit2.nothing}
1935
+ ${this._renderFormGrid(groupFields, cols, gap, ctx)}
1936
+ </fieldset>
1937
+ `;
1938
+ });
1939
+ const remaining = allFields.filter((f) => !usedKeys.has(f.key));
1940
+ return import_lit2.html`
1941
+ ${groupFragments}
1942
+ ${remaining.length ? this._renderFormGrid(remaining, layout.columns ?? 1, gap, ctx) : import_lit2.nothing}
1943
+ `;
1944
+ }
1945
+ _renderForm(fields, layout, ctx) {
1946
+ const ordered = this._getOrderedFields(fields, layout);
1947
+ const gap = layout.gap ?? "16px";
1948
+ if (layout.groups?.length) {
1949
+ return this._renderGroupedForm(ordered, layout.groups, layout, ctx);
1950
+ }
1951
+ return this._renderFormGrid(ordered, layout.columns ?? 1, gap, ctx);
1952
+ }
1953
+ _renderFormField(field, ctx) {
1954
+ const value = ctx.getValue(field.key);
1955
+ const error = ctx.getError(field.key);
1956
+ const touched = ctx.getTouched(field.key);
1957
+ const showError = touched && !!error;
1958
+ if (field.inputType === "checkbox") {
1959
+ return import_lit2.html`
1960
+ <div class="form-field" data-type="checkbox" ?data-error=${showError}>
1961
+ <label class="form-checkbox-label">
1962
+ <input
1963
+ type="checkbox"
1964
+ class="form-checkbox"
1965
+ .checked=${!!value}
1966
+ @change=${(e) => ctx.onUpdate(field.key, e.target.checked)}
1967
+ @blur=${() => ctx.onBlur(field.key)}
1968
+ />
1969
+ <span class="form-checkbox-text">
1970
+ ${field.label}
1971
+ ${field.required ? import_lit2.html`<span class="form-required">*</span>` : import_lit2.nothing}
1972
+ </span>
1973
+ </label>
1974
+ ${showError ? import_lit2.html`<span class="form-error">${error}</span>` : import_lit2.nothing}
1975
+ </div>
1976
+ `;
1977
+ }
1978
+ return import_lit2.html`
1979
+ <div class="form-field" data-type=${field.inputType} ?data-error=${showError}>
1980
+ <label class="form-label" for="form-${field.key}">
1981
+ ${field.label} ${field.required ? import_lit2.html`<span class="form-required">*</span>` : import_lit2.nothing}
1982
+ </label>
1983
+ ${field.description ? import_lit2.html`<span class="form-hint">${field.description}</span>` : import_lit2.nothing}
1984
+ ${this._renderFormInput(field, value, ctx)}
1985
+ ${showError ? import_lit2.html`<span class="form-error">${error}</span>` : import_lit2.nothing}
1986
+ </div>
1987
+ `;
1988
+ }
1989
+ _renderFormInput(field, value, ctx) {
1990
+ switch (field.inputType) {
1991
+ case "select":
1992
+ return import_lit2.html`
1993
+ <select
1994
+ class="form-select"
1995
+ id="form-${field.key}"
1996
+ @change=${(e) => ctx.onUpdate(field.key, e.target.value)}
1997
+ @blur=${() => ctx.onBlur(field.key)}
1998
+ >
1999
+ <option value="" ?selected=${!value}>選択してください</option>
2000
+ ${field.options.map(
2001
+ (opt) => import_lit2.html`<option value=${opt} ?selected=${value === opt}>${opt}</option>`
2002
+ )}
2003
+ </select>
2004
+ `;
2005
+ case "number":
2006
+ return import_lit2.html`
2007
+ <input
2008
+ type="number"
2009
+ class="form-input"
2010
+ id="form-${field.key}"
2011
+ .value=${value != null ? String(value) : ""}
2012
+ min=${field.min ?? import_lit2.nothing}
2013
+ max=${field.max ?? import_lit2.nothing}
2014
+ placeholder=${field.placeholder || import_lit2.nothing}
2015
+ @input=${(e) => {
2016
+ const v = e.target.valueAsNumber;
2017
+ ctx.onUpdate(field.key, Number.isNaN(v) ? void 0 : v);
2018
+ }}
2019
+ @blur=${() => ctx.onBlur(field.key)}
2020
+ />
2021
+ `;
2022
+ case "date":
2023
+ return import_lit2.html`
2024
+ <input
2025
+ type="date"
2026
+ class="form-input"
2027
+ id="form-${field.key}"
2028
+ .value=${value != null ? String(value) : ""}
2029
+ @input=${(e) => {
2030
+ const str = e.target.value;
2031
+ ctx.onUpdate(field.key, str || void 0);
2032
+ }}
2033
+ @blur=${() => ctx.onBlur(field.key)}
2034
+ />
2035
+ `;
2036
+ default:
2037
+ return import_lit2.html`
2038
+ <input
2039
+ type=${field.inputType}
2040
+ class="form-input"
2041
+ id="form-${field.key}"
2042
+ .value=${value ?? ""}
2043
+ minlength=${field.minLength ?? import_lit2.nothing}
2044
+ maxlength=${field.maxLength ?? import_lit2.nothing}
2045
+ placeholder=${field.placeholder || import_lit2.nothing}
2046
+ @input=${(e) => ctx.onUpdate(field.key, e.target.value)}
2047
+ @blur=${() => ctx.onBlur(field.key)}
2048
+ />
2049
+ `;
2050
+ }
2051
+ }
1247
2052
  _renderButtons() {
1248
2053
  const s = this._state;
1249
2054
  if (!s.showConfirmButton && !s.showCancelButton) return import_lit2.nothing;
@@ -1258,6 +2063,39 @@ var OverlayDialog = class extends import_lit2.LitElement {
1258
2063
  </div>
1259
2064
  `;
1260
2065
  }
2066
+ // ─── Step Form Helpers ───────────────────────────────────
2067
+ _renderStepFormButtons() {
2068
+ const s = this._state;
2069
+ const isFirst = s.stepFormCurrentIndex === 0;
2070
+ const isLast = s.stepFormCurrentIndex === s.stepFormSteps.length - 1;
2071
+ const submitText = isLast ? s.stepFormSubmitText : s.stepFormNextText;
2072
+ return import_lit2.html`
2073
+ <div class="actions actions-step-form">
2074
+ <button class="btn btn-cancel" @click=${() => this.controller.onCancel()}>
2075
+ ${s.cancelButtonText}
2076
+ </button>
2077
+ <div class="step-form-nav">
2078
+ ${isFirst ? import_lit2.nothing : import_lit2.html`
2079
+ <button class="btn btn-prev" @click=${() => this.controller.onStepPrev()}>
2080
+ ${s.stepFormPrevText}
2081
+ </button>
2082
+ `}
2083
+ <button class="btn btn-confirm" @click=${() => this.controller.onStepNext()}>
2084
+ ${submitText}
2085
+ </button>
2086
+ </div>
2087
+ </div>
2088
+ `;
2089
+ }
2090
+ _deriveStepItems() {
2091
+ const s = this._state;
2092
+ return s.stepFormSteps.map((st, i) => {
2093
+ let status = "pending";
2094
+ if (i < s.stepFormCurrentIndex) status = "done";
2095
+ else if (i === s.stepFormCurrentIndex) status = "active";
2096
+ return { key: st.key, label: st.label, status };
2097
+ });
2098
+ }
1261
2099
  _renderBody() {
1262
2100
  const s = this._state;
1263
2101
  switch (s.dialogType) {
@@ -1284,16 +2122,47 @@ var OverlayDialog = class extends import_lit2.LitElement {
1284
2122
  ${s.label ? import_lit2.html`<p class="label">${s.label}</p>` : import_lit2.nothing}
1285
2123
  ${this._renderStepsList(s.steps)}
1286
2124
  `;
2125
+ case "form": {
2126
+ const ctx = this._createFormContext();
2127
+ return import_lit2.html`
2128
+ ${s.title ? import_lit2.html`<p class="label">${s.title}</p>` : import_lit2.nothing}
2129
+ ${s.description ? import_lit2.html`<p class="description">${s.description}</p>` : import_lit2.nothing}
2130
+ <div class="form-scroll-container">
2131
+ ${this._renderForm(s.formFields, s.formLayout, ctx)}
2132
+ </div>
2133
+ ${this._renderButtons()}
2134
+ `;
2135
+ }
2136
+ case "step-form": {
2137
+ const stepItems = this._deriveStepItems();
2138
+ const currentStep = s.stepFormSteps[s.stepFormCurrentIndex];
2139
+ if (!currentStep) return import_lit2.html`${import_lit2.nothing}`;
2140
+ const ctx = this._createStepFormContext();
2141
+ const stepCount = s.stepFormSteps.length;
2142
+ const counterText = `${s.stepFormCurrentIndex + 1} / ${stepCount}`;
2143
+ return import_lit2.html`
2144
+ ${this._renderStepsHeader(stepItems)}
2145
+ <p class="step-form-counter">${counterText}</p>
2146
+ <p class="label">${currentStep.label}</p>
2147
+ ${currentStep.description ? import_lit2.html`<p class="description">${currentStep.description}</p>` : import_lit2.nothing}
2148
+ ${currentStep.fields.length ? import_lit2.html`
2149
+ <div class="form-scroll-container">
2150
+ ${this._renderForm(currentStep.fields, currentStep.layout, ctx)}
2151
+ </div>
2152
+ ` : import_lit2.nothing}
2153
+ ${this._renderStepFormButtons()}
2154
+ `;
2155
+ }
1287
2156
  default:
1288
2157
  return import_lit2.html`${import_lit2.nothing}`;
1289
2158
  }
1290
2159
  }
1291
2160
  render() {
1292
2161
  const s = this._state;
1293
- const showHeaderTitle = s.title && s.dialogType !== "alert" && s.dialogType !== "confirm";
2162
+ const showHeaderTitle = s.title && s.dialogType !== "alert" && s.dialogType !== "confirm" && s.dialogType !== "form" && s.dialogType !== "step-form";
1294
2163
  return import_lit2.html`
1295
2164
  <div class="backdrop" ?data-open=${s.open} @click=${this._onBackdropClick}>
1296
- <div class="card" ?data-closing=${this._isClosing}>
2165
+ <div class="card" data-type=${s.dialogType} ?data-closing=${this._isClosing}>
1297
2166
  ${showHeaderTitle ? import_lit2.html`<p class="dialog-title">${s.title}</p>` : import_lit2.nothing}
1298
2167
  <div class="card-body">
1299
2168
  ${(0, import_keyed.keyed)(this._bodyKey, import_lit2.html`<div class="body-inner">${this._renderBody()}</div>`)}
@@ -1349,6 +2218,28 @@ var DialogSingleton = class {
1349
2218
  __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1350
2219
  return __privateGet(this, _controller).confirm(optionsOrLabel);
1351
2220
  }
2221
+ // ─── Form ──────────────────────────────────────────────────
2222
+ form(schema, options) {
2223
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
2224
+ return __privateGet(this, _controller).form(schema, options);
2225
+ }
2226
+ // ─── Step Form ─────────────────────────────────────────────
2227
+ showStepForm(steps, options) {
2228
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
2229
+ return __privateGet(this, _controller).showStepForm(steps, options);
2230
+ }
2231
+ onStepNext() {
2232
+ __privateGet(this, _controller).onStepNext();
2233
+ }
2234
+ onStepPrev() {
2235
+ __privateGet(this, _controller).onStepPrev();
2236
+ }
2237
+ updateStepFormField(fieldKey, value) {
2238
+ __privateGet(this, _controller).updateStepFormField(fieldKey, value);
2239
+ }
2240
+ touchStepFormField(fieldKey) {
2241
+ __privateGet(this, _controller).touchStepFormField(fieldKey);
2242
+ }
1352
2243
  // ─── Loading helpers ─────────────────────────────────────
1353
2244
  showLoading(label) {
1354
2245
  __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
@@ -1429,10 +2320,909 @@ ensureElement_fn = function() {
1429
2320
  __privateSet(this, _element, el);
1430
2321
  };
1431
2322
  var dialog = new DialogSingleton();
2323
+
2324
+ // src/toast/types.ts
2325
+ var createInitialToastState = () => ({
2326
+ items: [],
2327
+ position: "top-right",
2328
+ maxVisible: 3,
2329
+ defaultDuration: 4e3
2330
+ });
2331
+
2332
+ // src/toast/controller.ts
2333
+ var DISMISS_ANIMATION_MS = 400;
2334
+ var _state2, _listeners2, _timers, _timerStartedAt, _dismissTimers, _nextId, _ToastController_instances, emit_fn2, update_fn2, startTimer_fn, clearTimer_fn2, beginDismiss_fn, removeItem_fn, updateItem_fn, findItem_fn, enforceMaxVisible_fn;
2335
+ var ToastController = class {
2336
+ constructor() {
2337
+ __privateAdd(this, _ToastController_instances);
2338
+ __privateAdd(this, _state2);
2339
+ __privateAdd(this, _listeners2, /* @__PURE__ */ new Set());
2340
+ // ─── Per-toast timer management ─────────────────────────
2341
+ __privateAdd(this, _timers, /* @__PURE__ */ new Map());
2342
+ __privateAdd(this, _timerStartedAt, /* @__PURE__ */ new Map());
2343
+ __privateAdd(this, _dismissTimers, /* @__PURE__ */ new Map());
2344
+ __privateAdd(this, _nextId, 0);
2345
+ __privateSet(this, _state2, createInitialToastState());
2346
+ }
2347
+ // ─── Observable ─────────────────────────────────────────
2348
+ get state() {
2349
+ return __privateGet(this, _state2);
2350
+ }
2351
+ subscribe(fn) {
2352
+ __privateGet(this, _listeners2).add(fn);
2353
+ return () => __privateGet(this, _listeners2).delete(fn);
2354
+ }
2355
+ // ─── Configuration ──────────────────────────────────────
2356
+ configure(config) {
2357
+ const patch = {};
2358
+ if (config.position != null) patch.position = config.position;
2359
+ if (config.maxVisible != null) patch.maxVisible = config.maxVisible;
2360
+ if (config.defaultDuration != null) patch.defaultDuration = config.defaultDuration;
2361
+ if (Object.keys(patch).length > 0) __privateMethod(this, _ToastController_instances, update_fn2).call(this, patch);
2362
+ }
2363
+ // ─── Show ───────────────────────────────────────────────
2364
+ show(options) {
2365
+ const id = `toast-${++__privateWrapper(this, _nextId)._}`;
2366
+ const duration = options.duration ?? __privateGet(this, _state2).defaultDuration;
2367
+ const item = {
2368
+ id,
2369
+ type: options.type ?? "info",
2370
+ message: options.message,
2371
+ description: options.description ?? "",
2372
+ action: options.action ?? null,
2373
+ duration,
2374
+ remainingMs: duration,
2375
+ paused: false,
2376
+ dismissing: false
2377
+ };
2378
+ const items = [...__privateGet(this, _state2).items, item];
2379
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2380
+ if (duration > 0) {
2381
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, duration);
2382
+ }
2383
+ __privateMethod(this, _ToastController_instances, enforceMaxVisible_fn).call(this);
2384
+ return id;
2385
+ }
2386
+ success(message, options) {
2387
+ return this.show({ ...options, type: "success", message });
2388
+ }
2389
+ error(message, options) {
2390
+ return this.show({ ...options, type: "error", message });
2391
+ }
2392
+ warning(message, options) {
2393
+ return this.show({ ...options, type: "warning", message });
2394
+ }
2395
+ info(message, options) {
2396
+ return this.show({ ...options, type: "info", message });
2397
+ }
2398
+ loading(message, options) {
2399
+ return this.show({ duration: 0, ...options, type: "loading", message });
2400
+ }
2401
+ // ─── Dismiss ────────────────────────────────────────────
2402
+ dismiss(id) {
2403
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2404
+ if (!item || item.dismissing) return;
2405
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, id);
2406
+ }
2407
+ dismissAll() {
2408
+ for (const item of __privateGet(this, _state2).items) {
2409
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, item.id);
2410
+ __privateGet(this, _timerStartedAt).delete(item.id);
2411
+ }
2412
+ const items = __privateGet(this, _state2).items.filter((i) => !i.dismissing).map((item) => ({ ...item, dismissing: true, paused: true }));
2413
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2414
+ const timer = setTimeout(() => {
2415
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items: [] });
2416
+ __privateGet(this, _dismissTimers).clear();
2417
+ }, DISMISS_ANIMATION_MS);
2418
+ __privateGet(this, _dismissTimers).set("__all__", timer);
2419
+ }
2420
+ // ─── Update ─────────────────────────────────────────────
2421
+ update(id, patch) {
2422
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2423
+ if (!item || item.dismissing) return;
2424
+ const { duration: newDuration, ...rest } = patch;
2425
+ const isLeavingLoading = item.type === "loading" && rest.type != null && rest.type !== "loading";
2426
+ if (newDuration !== void 0) {
2427
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2428
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { ...rest, duration: newDuration, remainingMs: newDuration, paused: false });
2429
+ if (newDuration > 0) {
2430
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, newDuration);
2431
+ }
2432
+ } else if (isLeavingLoading) {
2433
+ const autoMs = __privateGet(this, _state2).defaultDuration;
2434
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { ...rest, duration: autoMs, remainingMs: autoMs, paused: false });
2435
+ if (autoMs > 0) {
2436
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, autoMs);
2437
+ }
2438
+ } else {
2439
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, rest);
2440
+ }
2441
+ }
2442
+ // ─── Timer control (called from component) ─────────────
2443
+ pauseTimer(id) {
2444
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2445
+ if (!item || item.paused || item.dismissing || item.duration <= 0) return;
2446
+ const startedAt = __privateGet(this, _timerStartedAt).get(id);
2447
+ if (startedAt == null) return;
2448
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2449
+ const elapsed = Date.now() - startedAt;
2450
+ const remainingMs = Math.max(0, item.remainingMs - elapsed);
2451
+ __privateGet(this, _timerStartedAt).delete(id);
2452
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { paused: true, remainingMs });
2453
+ }
2454
+ resumeTimer(id) {
2455
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2456
+ if (!item || !item.paused || item.dismissing || item.duration <= 0) return;
2457
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { paused: false });
2458
+ const updated = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2459
+ if (updated && updated.remainingMs > 0) {
2460
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, updated.remainingMs);
2461
+ }
2462
+ }
2463
+ };
2464
+ _state2 = new WeakMap();
2465
+ _listeners2 = new WeakMap();
2466
+ _timers = new WeakMap();
2467
+ _timerStartedAt = new WeakMap();
2468
+ _dismissTimers = new WeakMap();
2469
+ _nextId = new WeakMap();
2470
+ _ToastController_instances = new WeakSet();
2471
+ emit_fn2 = function() {
2472
+ const snapshot = { ...__privateGet(this, _state2), items: [...__privateGet(this, _state2).items] };
2473
+ for (const fn of __privateGet(this, _listeners2)) fn(snapshot);
2474
+ };
2475
+ update_fn2 = function(patch) {
2476
+ Object.assign(__privateGet(this, _state2), patch);
2477
+ __privateMethod(this, _ToastController_instances, emit_fn2).call(this);
2478
+ };
2479
+ // ─── Internal ───────────────────────────────────────────
2480
+ startTimer_fn = function(id, durationMs) {
2481
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2482
+ __privateGet(this, _timerStartedAt).set(id, Date.now());
2483
+ __privateGet(this, _timers).set(
2484
+ id,
2485
+ setTimeout(() => {
2486
+ __privateGet(this, _timers).delete(id);
2487
+ __privateGet(this, _timerStartedAt).delete(id);
2488
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, id);
2489
+ }, durationMs)
2490
+ );
2491
+ };
2492
+ clearTimer_fn2 = function(id) {
2493
+ const timer = __privateGet(this, _timers).get(id);
2494
+ if (timer != null) {
2495
+ clearTimeout(timer);
2496
+ __privateGet(this, _timers).delete(id);
2497
+ }
2498
+ };
2499
+ beginDismiss_fn = function(id) {
2500
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2501
+ __privateGet(this, _timerStartedAt).delete(id);
2502
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2503
+ if (!item || item.dismissing) return;
2504
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { dismissing: true, paused: true });
2505
+ __privateGet(this, _dismissTimers).set(
2506
+ id,
2507
+ setTimeout(() => {
2508
+ __privateMethod(this, _ToastController_instances, removeItem_fn).call(this, id);
2509
+ __privateGet(this, _dismissTimers).delete(id);
2510
+ }, DISMISS_ANIMATION_MS)
2511
+ );
2512
+ };
2513
+ removeItem_fn = function(id) {
2514
+ const items = __privateGet(this, _state2).items.filter((i) => i.id !== id);
2515
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2516
+ };
2517
+ updateItem_fn = function(id, patch) {
2518
+ const items = __privateGet(this, _state2).items.map((item) => item.id === id ? { ...item, ...patch } : item);
2519
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2520
+ };
2521
+ findItem_fn = function(id) {
2522
+ return __privateGet(this, _state2).items.find((i) => i.id === id);
2523
+ };
2524
+ enforceMaxVisible_fn = function() {
2525
+ const active = __privateGet(this, _state2).items.filter((i) => !i.dismissing);
2526
+ const excess = active.length - __privateGet(this, _state2).maxVisible;
2527
+ if (excess <= 0) return;
2528
+ for (let i = 0; i < excess; i++) {
2529
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, active[i].id);
2530
+ }
2531
+ };
2532
+
2533
+ // src/toast/toast-container.ts
2534
+ var import_lit4 = require("lit");
2535
+ var import_decorators2 = require("lit/decorators.js");
2536
+ var import_repeat = require("lit/directives/repeat.js");
2537
+
2538
+ // src/toast/styles.ts
2539
+ var import_lit3 = require("lit");
2540
+ var toastStyles = import_lit3.css`
2541
+ :host {
2542
+ /* ─── Customizable CSS Variables ─── */
2543
+ --toast-font-family: var(
2544
+ --dialog-font-family,
2545
+ 'Yu Gothic Medium',
2546
+ '游ゴシック',
2547
+ YuGothic,
2548
+ 'メイリオ',
2549
+ 'Hiragino Kaku Gothic ProN',
2550
+ Meiryo,
2551
+ sans-serif
2552
+ );
2553
+ --toast-text-color: var(--dialog-text-color, #356);
2554
+ --toast-z-index: 1100;
2555
+
2556
+ /* Container */
2557
+ --toast-gap: 12px;
2558
+ --toast-padding: 16px;
2559
+ --toast-max-width: 420px;
2560
+
2561
+ /* Card */
2562
+ --toast-card-bg: #fff;
2563
+ --toast-card-border: #f3f4f6;
2564
+ --toast-card-shadow: 0 4px 12px rgb(0 0 0 / 0.08), 0 1px 3px rgb(0 0 0 / 0.06);
2565
+ --toast-card-radius: 8px;
2566
+ --toast-card-padding: 14px 16px;
2567
+
2568
+ /* Colors (inherit from dialog when available) */
2569
+ --toast-success: var(--dialog-success, #22c55e);
2570
+ --toast-error: var(--dialog-error, #ef4444);
2571
+ --toast-warning: var(--dialog-warning, #f59e0b);
2572
+ --toast-info: var(--dialog-info, #3b82f6);
2573
+ --toast-loading: var(--dialog-primary, #3b82f6);
2574
+ --toast-spinner-track: rgb(59 130 246 / 0.2);
2575
+ --toast-spinner-arc: var(--toast-loading);
2576
+
2577
+ /* Progress bar */
2578
+ --toast-progress-height: 3px;
2579
+
2580
+ /* Close button */
2581
+ --toast-close-color: #9ca3af;
2582
+ --toast-close-hover-color: #374151;
2583
+
2584
+ /* Timing */
2585
+ --toast-enter-duration: 350ms;
2586
+ --toast-exit-duration: 200ms;
2587
+ --toast-collapse-duration: 200ms;
2588
+
2589
+ display: contents;
2590
+ font-family: var(--toast-font-family);
2591
+ color: var(--toast-text-color);
2592
+ }
2593
+
2594
+ /* ─── Container ─── */
2595
+
2596
+ .container {
2597
+ position: fixed;
2598
+ z-index: var(--toast-z-index);
2599
+ display: flex;
2600
+ flex-direction: column;
2601
+ gap: var(--toast-gap);
2602
+ padding: var(--toast-padding);
2603
+ pointer-events: none;
2604
+ max-height: 100vh;
2605
+ max-width: var(--toast-max-width);
2606
+ box-sizing: border-box;
2607
+ }
2608
+
2609
+ /* Position variants */
2610
+ .container[data-position='top-right'] {
2611
+ top: 0;
2612
+ right: 0;
2613
+ }
2614
+ .container[data-position='top-left'] {
2615
+ top: 0;
2616
+ left: 0;
2617
+ }
2618
+ .container[data-position='top-center'] {
2619
+ top: 0;
2620
+ left: 50%;
2621
+ transform: translateX(-50%);
2622
+ }
2623
+ .container[data-position='bottom-right'] {
2624
+ bottom: 0;
2625
+ right: 0;
2626
+ flex-direction: column-reverse;
2627
+ }
2628
+ .container[data-position='bottom-left'] {
2629
+ bottom: 0;
2630
+ left: 0;
2631
+ flex-direction: column-reverse;
2632
+ }
2633
+ .container[data-position='bottom-center'] {
2634
+ bottom: 0;
2635
+ left: 50%;
2636
+ transform: translateX(-50%);
2637
+ flex-direction: column-reverse;
2638
+ }
2639
+
2640
+ /* Responsive: full-width on mobile */
2641
+ @media (max-width: 639px) {
2642
+ .container {
2643
+ max-width: 100%;
2644
+ width: 100%;
2645
+ left: 0;
2646
+ right: 0;
2647
+ transform: none;
2648
+ }
2649
+ .container[data-position^='top'] {
2650
+ top: 0;
2651
+ flex-direction: column;
2652
+ }
2653
+ .container[data-position^='bottom'] {
2654
+ bottom: 0;
2655
+ flex-direction: column-reverse;
2656
+ }
2657
+ }
2658
+
2659
+ /* ─── Direction-based transform variables ─── */
2660
+
2661
+ .container[data-position$='right'] {
2662
+ --_enter-x: 110%;
2663
+ --_exit-x: 110%;
2664
+ --_enter-y: 0;
2665
+ --_exit-y: 0;
2666
+ }
2667
+ .container[data-position$='left'] {
2668
+ --_enter-x: -110%;
2669
+ --_exit-x: -110%;
2670
+ --_enter-y: 0;
2671
+ --_exit-y: 0;
2672
+ }
2673
+ .container[data-position='top-center'] {
2674
+ --_enter-x: 0;
2675
+ --_exit-x: 0;
2676
+ --_enter-y: -100%;
2677
+ --_exit-y: -100%;
2678
+ }
2679
+ .container[data-position='bottom-center'] {
2680
+ --_enter-x: 0;
2681
+ --_exit-x: 0;
2682
+ --_enter-y: 100%;
2683
+ --_exit-y: 100%;
2684
+ }
2685
+
2686
+ @media (max-width: 639px) {
2687
+ .container[data-position^='top'] {
2688
+ --_enter-x: 0;
2689
+ --_exit-x: 0;
2690
+ --_enter-y: -100%;
2691
+ --_exit-y: -100%;
2692
+ }
2693
+ .container[data-position^='bottom'] {
2694
+ --_enter-x: 0;
2695
+ --_exit-x: 0;
2696
+ --_enter-y: 100%;
2697
+ --_exit-y: 100%;
2698
+ }
2699
+ }
2700
+
2701
+ /* ─── Toast Slot (height-collapsing wrapper) ─── */
2702
+
2703
+ .toast-slot {
2704
+ display: grid;
2705
+ grid-template-rows: 1fr;
2706
+ transition: grid-template-rows var(--toast-collapse-duration) ease var(--toast-exit-duration);
2707
+ pointer-events: auto;
2708
+ }
2709
+
2710
+ .toast-slot[data-dismissing] {
2711
+ grid-template-rows: 0fr;
2712
+ }
2713
+
2714
+ .toast-slot > .toast-card {
2715
+ overflow: hidden;
2716
+ min-height: 0;
2717
+ }
2718
+
2719
+ /* ─── Toast Card ─── */
2720
+
2721
+ .toast-card {
2722
+ background: var(--toast-card-bg);
2723
+ border: 1px solid var(--toast-card-border);
2724
+ border-radius: var(--toast-card-radius);
2725
+ box-shadow: var(--toast-card-shadow);
2726
+ position: relative;
2727
+ overflow: hidden;
2728
+ animation: toast-enter var(--toast-enter-duration) cubic-bezier(0.16, 1, 0.3, 1) both;
2729
+ }
2730
+
2731
+ .toast-card[data-dismissing] {
2732
+ animation: toast-exit var(--toast-exit-duration) ease both;
2733
+ pointer-events: none;
2734
+ }
2735
+
2736
+ /* Type-based left accent */
2737
+ .toast-card[data-type='success'] {
2738
+ border-left: 3px solid var(--toast-success);
2739
+ }
2740
+ .toast-card[data-type='error'] {
2741
+ border-left: 3px solid var(--toast-error);
2742
+ }
2743
+ .toast-card[data-type='warning'] {
2744
+ border-left: 3px solid var(--toast-warning);
2745
+ }
2746
+ .toast-card[data-type='info'] {
2747
+ border-left: 3px solid var(--toast-info);
2748
+ }
2749
+ .toast-card[data-type='loading'] {
2750
+ border-left: 3px solid var(--toast-loading);
2751
+ }
2752
+
2753
+ /* ─── Animations ─── */
2754
+
2755
+ @keyframes toast-enter {
2756
+ from {
2757
+ opacity: 0;
2758
+ transform: translateX(var(--_enter-x, 0)) translateY(var(--_enter-y, 0));
2759
+ filter: blur(4px);
2760
+ }
2761
+ to {
2762
+ opacity: 1;
2763
+ transform: translateX(0) translateY(0);
2764
+ filter: blur(0);
2765
+ }
2766
+ }
2767
+
2768
+ @keyframes toast-exit {
2769
+ from {
2770
+ opacity: 1;
2771
+ transform: translateX(0) translateY(0);
2772
+ filter: blur(0);
2773
+ }
2774
+ to {
2775
+ opacity: 0;
2776
+ transform: translateX(var(--_exit-x, 0)) translateY(var(--_exit-y, 0));
2777
+ filter: blur(3px);
2778
+ }
2779
+ }
2780
+
2781
+ /* ─── Body ─── */
2782
+
2783
+ .toast-body {
2784
+ display: flex;
2785
+ align-items: flex-start;
2786
+ gap: 12px;
2787
+ padding: var(--toast-card-padding);
2788
+ }
2789
+
2790
+ /* ─── Icon ─── */
2791
+
2792
+ .toast-icon {
2793
+ width: 20px;
2794
+ height: 20px;
2795
+ flex-shrink: 0;
2796
+ margin-top: 1px;
2797
+ display: flex;
2798
+ align-items: center;
2799
+ justify-content: center;
2800
+ }
2801
+
2802
+ .toast-icon svg {
2803
+ width: 20px;
2804
+ height: 20px;
2805
+ }
2806
+
2807
+ .icon-success {
2808
+ color: var(--toast-success);
2809
+ }
2810
+ .icon-error {
2811
+ color: var(--toast-error);
2812
+ }
2813
+ .icon-warning {
2814
+ color: var(--toast-warning);
2815
+ }
2816
+ .icon-info {
2817
+ color: var(--toast-info);
2818
+ }
2819
+ .icon-loading {
2820
+ color: var(--toast-loading);
2821
+ }
2822
+
2823
+ /* ─── Loading Spinner ─── */
2824
+
2825
+ @keyframes toast-spin {
2826
+ to {
2827
+ transform: rotate(360deg);
2828
+ }
2829
+ }
2830
+
2831
+ .toast-spinner {
2832
+ width: 20px;
2833
+ height: 20px;
2834
+ border-radius: 50%;
2835
+ box-shadow: inset 0 0 0 2px var(--toast-spinner-track);
2836
+ position: relative;
2837
+ animation: toast-spin 1.2s infinite linear;
2838
+ flex-shrink: 0;
2839
+ }
2840
+
2841
+ .toast-spinner-half {
2842
+ position: absolute;
2843
+ left: 50%;
2844
+ top: 50%;
2845
+ width: 10px;
2846
+ height: 20px;
2847
+ margin-left: -10px;
2848
+ margin-top: -10px;
2849
+ overflow: hidden;
2850
+ transform-origin: 10px 10px;
2851
+ mask-image: linear-gradient(to bottom, #000f, #0000);
2852
+ -webkit-mask-image: linear-gradient(to bottom, #000f, #0000);
2853
+ }
2854
+
2855
+ .toast-spinner-inner {
2856
+ width: 20px;
2857
+ height: 20px;
2858
+ border-radius: 50%;
2859
+ box-shadow: inset 0 0 0 2px var(--toast-spinner-arc);
2860
+ }
2861
+
2862
+ /* ─── Text ─── */
2863
+
2864
+ .toast-text {
2865
+ flex: 1;
2866
+ min-width: 0;
2867
+ display: flex;
2868
+ flex-direction: column;
2869
+ gap: 4px;
2870
+ }
2871
+
2872
+ .toast-message {
2873
+ font-size: 14px;
2874
+ font-weight: 500;
2875
+ color: #1f2937;
2876
+ margin: 0;
2877
+ word-break: break-word;
2878
+ line-height: 1.4;
2879
+ }
2880
+
2881
+ .toast-description {
2882
+ font-size: 13px;
2883
+ color: #6b7280;
2884
+ margin: 0;
2885
+ word-break: break-word;
2886
+ line-height: 1.5;
2887
+ }
2888
+
2889
+ /* ─── Action Button ─── */
2890
+
2891
+ .toast-action-btn {
2892
+ background: none;
2893
+ border: none;
2894
+ padding: 0;
2895
+ font-family: inherit;
2896
+ font-size: 13px;
2897
+ font-weight: 600;
2898
+ color: var(--toast-info);
2899
+ cursor: pointer;
2900
+ align-self: flex-start;
2901
+ text-decoration: underline;
2902
+ text-underline-offset: 2px;
2903
+ transition: color 150ms ease;
2904
+ }
2905
+
2906
+ .toast-action-btn:hover {
2907
+ color: var(--dialog-primary-hover, #2563eb);
2908
+ }
2909
+
2910
+ /* ─── Close Button ─── */
2911
+
2912
+ .toast-close {
2913
+ width: 20px;
2914
+ height: 20px;
2915
+ padding: 0;
2916
+ margin: 0;
2917
+ border: none;
2918
+ background: none;
2919
+ cursor: pointer;
2920
+ flex-shrink: 0;
2921
+ display: flex;
2922
+ align-items: center;
2923
+ justify-content: center;
2924
+ color: var(--toast-close-color);
2925
+ transition: color 150ms ease;
2926
+ border-radius: 4px;
2927
+ }
2928
+
2929
+ .toast-close svg {
2930
+ width: 14px;
2931
+ height: 14px;
2932
+ }
2933
+
2934
+ .toast-close:hover {
2935
+ color: var(--toast-close-hover-color);
2936
+ }
2937
+
2938
+ @media (min-width: 640px) {
2939
+ .toast-close {
2940
+ opacity: 0.5;
2941
+ transition:
2942
+ opacity 150ms ease,
2943
+ color 150ms ease;
2944
+ }
2945
+ .toast-card:hover .toast-close {
2946
+ opacity: 1;
2947
+ }
2948
+ }
2949
+
2950
+ /* ─── Progress Bar ─── */
2951
+
2952
+ .toast-progress {
2953
+ position: absolute;
2954
+ bottom: 0;
2955
+ left: 0;
2956
+ height: var(--toast-progress-height);
2957
+ width: 100%;
2958
+ border-radius: 0 0 var(--toast-card-radius) var(--toast-card-radius);
2959
+ animation: toast-progress-countdown linear forwards;
2960
+ animation-play-state: running;
2961
+ }
2962
+
2963
+ .toast-card[data-paused] .toast-progress {
2964
+ animation-play-state: paused;
2965
+ }
2966
+
2967
+ @keyframes toast-progress-countdown {
2968
+ from {
2969
+ width: 100%;
2970
+ }
2971
+ to {
2972
+ width: 0%;
2973
+ }
2974
+ }
2975
+
2976
+ .toast-progress[data-type='success'] {
2977
+ background-color: var(--toast-success);
2978
+ }
2979
+ .toast-progress[data-type='error'] {
2980
+ background-color: var(--toast-error);
2981
+ }
2982
+ .toast-progress[data-type='warning'] {
2983
+ background-color: var(--toast-warning);
2984
+ }
2985
+ .toast-progress[data-type='info'] {
2986
+ background-color: var(--toast-info);
2987
+ }
2988
+ `;
2989
+
2990
+ // src/toast/toast-container.ts
2991
+ var ToastContainer = class extends import_lit4.LitElement {
2992
+ constructor() {
2993
+ super(...arguments);
2994
+ this._state = createInitialToastState();
2995
+ }
2996
+ connectedCallback() {
2997
+ super.connectedCallback();
2998
+ if (this.controller) {
2999
+ this._state = { ...this.controller.state };
3000
+ this._unsubscribe = this.controller.subscribe((s) => {
3001
+ this._state = s;
3002
+ });
3003
+ }
3004
+ }
3005
+ disconnectedCallback() {
3006
+ super.disconnectedCallback();
3007
+ this._unsubscribe?.();
3008
+ }
3009
+ // ─── Icon Rendering ─────────────────────────────────────
3010
+ _renderIcon(type) {
3011
+ switch (type) {
3012
+ case "success":
3013
+ return import_lit4.html`<svg
3014
+ viewBox="0 0 24 24"
3015
+ fill="none"
3016
+ stroke="currentColor"
3017
+ stroke-width="2"
3018
+ stroke-linecap="round"
3019
+ stroke-linejoin="round"
3020
+ >
3021
+ <circle cx="12" cy="12" r="10" />
3022
+ <polyline points="9,12 11,14 15,10" />
3023
+ </svg>`;
3024
+ case "error":
3025
+ return import_lit4.html`<svg
3026
+ viewBox="0 0 24 24"
3027
+ fill="none"
3028
+ stroke="currentColor"
3029
+ stroke-width="2"
3030
+ stroke-linecap="round"
3031
+ stroke-linejoin="round"
3032
+ >
3033
+ <circle cx="12" cy="12" r="10" />
3034
+ <line x1="15" y1="9" x2="9" y2="15" />
3035
+ <line x1="9" y1="9" x2="15" y2="15" />
3036
+ </svg>`;
3037
+ case "warning":
3038
+ return import_lit4.html`<svg
3039
+ viewBox="0 0 24 24"
3040
+ fill="none"
3041
+ stroke="currentColor"
3042
+ stroke-width="2"
3043
+ stroke-linecap="round"
3044
+ stroke-linejoin="round"
3045
+ >
3046
+ <path
3047
+ d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"
3048
+ />
3049
+ <line x1="12" y1="9" x2="12" y2="13" />
3050
+ <line x1="12" y1="17" x2="12.01" y2="17" />
3051
+ </svg>`;
3052
+ case "info":
3053
+ return import_lit4.html`<svg
3054
+ viewBox="0 0 24 24"
3055
+ fill="none"
3056
+ stroke="currentColor"
3057
+ stroke-width="2"
3058
+ stroke-linecap="round"
3059
+ stroke-linejoin="round"
3060
+ >
3061
+ <circle cx="12" cy="12" r="10" />
3062
+ <line x1="12" y1="16" x2="12" y2="12" />
3063
+ <line x1="12" y1="8" x2="12.01" y2="8" />
3064
+ </svg>`;
3065
+ case "loading":
3066
+ return import_lit4.html`
3067
+ <div class="toast-spinner">
3068
+ <div class="toast-spinner-half">
3069
+ <div class="toast-spinner-inner"></div>
3070
+ </div>
3071
+ </div>
3072
+ `;
3073
+ }
3074
+ }
3075
+ // ─── Toast Rendering ────────────────────────────────────
3076
+ _renderToast(item) {
3077
+ return import_lit4.html`
3078
+ <div class="toast-slot" ?data-dismissing=${item.dismissing}>
3079
+ <div
3080
+ class="toast-card"
3081
+ data-type=${item.type}
3082
+ ?data-dismissing=${item.dismissing}
3083
+ ?data-paused=${item.paused}
3084
+ @mouseenter=${() => this.controller.pauseTimer(item.id)}
3085
+ @mouseleave=${() => this.controller.resumeTimer(item.id)}
3086
+ >
3087
+ <div class="toast-body">
3088
+ <span class="toast-icon icon-${item.type}"> ${this._renderIcon(item.type)} </span>
3089
+ <div class="toast-text">
3090
+ <p class="toast-message">${item.message}</p>
3091
+ ${item.description ? import_lit4.html`<p class="toast-description">${item.description}</p>` : import_lit4.nothing}
3092
+ ${item.action ? import_lit4.html`<button
3093
+ class="toast-action-btn"
3094
+ @click=${() => {
3095
+ item.action.onClick();
3096
+ this.controller.dismiss(item.id);
3097
+ }}
3098
+ >
3099
+ ${item.action.label}
3100
+ </button>` : import_lit4.nothing}
3101
+ </div>
3102
+ <button
3103
+ class="toast-close"
3104
+ @click=${() => this.controller.dismiss(item.id)}
3105
+ aria-label="閉じる"
3106
+ >
3107
+ <svg
3108
+ viewBox="0 0 24 24"
3109
+ fill="none"
3110
+ stroke="currentColor"
3111
+ stroke-width="2"
3112
+ stroke-linecap="round"
3113
+ stroke-linejoin="round"
3114
+ >
3115
+ <line x1="18" y1="6" x2="6" y2="18" />
3116
+ <line x1="6" y1="6" x2="18" y2="18" />
3117
+ </svg>
3118
+ </button>
3119
+ </div>
3120
+ ${item.duration > 0 && item.type !== "loading" ? import_lit4.html`<div
3121
+ class="toast-progress"
3122
+ style="animation-duration:${item.duration}ms"
3123
+ data-type=${item.type}
3124
+ ></div>` : import_lit4.nothing}
3125
+ </div>
3126
+ </div>
3127
+ `;
3128
+ }
3129
+ // ─── Main Render ────────────────────────────────────────
3130
+ render() {
3131
+ const s = this._state;
3132
+ return import_lit4.html`
3133
+ <div class="container" data-position=${s.position} role="region" aria-label="通知">
3134
+ ${(0, import_repeat.repeat)(
3135
+ s.items,
3136
+ (item) => item.id,
3137
+ (item) => this._renderToast(item)
3138
+ )}
3139
+ </div>
3140
+ `;
3141
+ }
3142
+ };
3143
+ ToastContainer.styles = toastStyles;
3144
+ __decorateClass([
3145
+ (0, import_decorators2.property)({ attribute: false })
3146
+ ], ToastContainer.prototype, "controller", 2);
3147
+ __decorateClass([
3148
+ (0, import_decorators2.state)()
3149
+ ], ToastContainer.prototype, "_state", 2);
3150
+ ToastContainer = __decorateClass([
3151
+ (0, import_decorators2.customElement)("toast-container")
3152
+ ], ToastContainer);
3153
+
3154
+ // src/toast/toast.ts
3155
+ var _controller2, _element2, _ToastSingleton_instances, ensureElement_fn2;
3156
+ var ToastSingleton = class {
3157
+ constructor() {
3158
+ __privateAdd(this, _ToastSingleton_instances);
3159
+ __privateAdd(this, _controller2, new ToastController());
3160
+ __privateAdd(this, _element2, null);
3161
+ }
3162
+ // ─── Configuration ──────────────────────────────────────
3163
+ configure(config) {
3164
+ __privateGet(this, _controller2).configure(config);
3165
+ }
3166
+ // ─── Show Methods ───────────────────────────────────────
3167
+ show(options) {
3168
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3169
+ return __privateGet(this, _controller2).show(options);
3170
+ }
3171
+ success(message, options) {
3172
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3173
+ return __privateGet(this, _controller2).success(message, options);
3174
+ }
3175
+ error(message, options) {
3176
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3177
+ return __privateGet(this, _controller2).error(message, options);
3178
+ }
3179
+ warning(message, options) {
3180
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3181
+ return __privateGet(this, _controller2).warning(message, options);
3182
+ }
3183
+ info(message, options) {
3184
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3185
+ return __privateGet(this, _controller2).info(message, options);
3186
+ }
3187
+ loading(message, options) {
3188
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3189
+ return __privateGet(this, _controller2).loading(message, options);
3190
+ }
3191
+ // ─── Dismiss ────────────────────────────────────────────
3192
+ dismiss(id) {
3193
+ __privateGet(this, _controller2).dismiss(id);
3194
+ }
3195
+ dismissAll() {
3196
+ __privateGet(this, _controller2).dismissAll();
3197
+ }
3198
+ // ─── Update ─────────────────────────────────────────────
3199
+ update(id, patch) {
3200
+ __privateGet(this, _controller2).update(id, patch);
3201
+ }
3202
+ // ─── Advanced ───────────────────────────────────────────
3203
+ get controller() {
3204
+ return __privateGet(this, _controller2);
3205
+ }
3206
+ };
3207
+ _controller2 = new WeakMap();
3208
+ _element2 = new WeakMap();
3209
+ _ToastSingleton_instances = new WeakSet();
3210
+ ensureElement_fn2 = function() {
3211
+ if (__privateGet(this, _element2)) return;
3212
+ if (typeof document === "undefined") return;
3213
+ const el = document.createElement("toast-container");
3214
+ el.controller = __privateGet(this, _controller2);
3215
+ document.body.appendChild(el);
3216
+ __privateSet(this, _element2, el);
3217
+ };
3218
+ var toast = new ToastSingleton();
1432
3219
  // Annotate the CommonJS export names for ESM import in node:
1433
3220
  0 && (module.exports = {
1434
3221
  DialogController,
1435
3222
  OverlayDialog,
1436
- dialog
3223
+ ToastContainer,
3224
+ ToastController,
3225
+ dialog,
3226
+ toast
1437
3227
  });
1438
3228
  //# sourceMappingURL=index.cjs.map