@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.js CHANGED
@@ -16,6 +16,14 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
16
16
  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);
17
17
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
18
18
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
19
+ var __privateWrapper = (obj, member, setter, getter) => ({
20
+ set _(value) {
21
+ __privateSet(obj, member, value, setter);
22
+ },
23
+ get _() {
24
+ return __privateGet(obj, member, getter);
25
+ }
26
+ });
19
27
 
20
28
  // src/types.ts
21
29
  var createInitialState = () => ({
@@ -35,14 +43,168 @@ var createInitialState = () => ({
35
43
  queues: [],
36
44
  steps: [],
37
45
  timer: null,
38
- title: ""
46
+ title: "",
47
+ formFields: [],
48
+ formValues: {},
49
+ formErrors: {},
50
+ formTouched: {},
51
+ formLayout: {},
52
+ formValidateOnChange: true,
53
+ formValidateOnBlur: true,
54
+ stepFormSteps: [],
55
+ stepFormCurrentIndex: 0,
56
+ stepFormNextText: "\u6B21\u3078",
57
+ stepFormPrevText: "\u623B\u308B",
58
+ stepFormSubmitText: "OK"
39
59
  });
40
60
 
61
+ // src/zod-utils.ts
62
+ function resolveTypeName(field) {
63
+ const raw = field._def.typeName ?? field._def.type;
64
+ if (!raw) return "";
65
+ const v4Map = {
66
+ string: "ZodString",
67
+ number: "ZodNumber",
68
+ boolean: "ZodBoolean",
69
+ enum: "ZodEnum",
70
+ date: "ZodDate",
71
+ optional: "ZodOptional",
72
+ nullable: "ZodNullable",
73
+ default: "ZodDefault",
74
+ object: "ZodObject",
75
+ pipe: "ZodPipe"
76
+ };
77
+ return v4Map[raw] ?? raw;
78
+ }
79
+ function unwrapType(zodType) {
80
+ let inner = zodType;
81
+ let required = true;
82
+ let defaultValue = void 0;
83
+ let description = zodType.description ?? "";
84
+ while (true) {
85
+ const typeName = resolveTypeName(inner);
86
+ if (typeName === "ZodOptional" || typeName === "ZodNullable") {
87
+ required = false;
88
+ inner = inner._def.innerType;
89
+ } else if (typeName === "ZodDefault") {
90
+ const raw = inner._def.defaultValue;
91
+ defaultValue = typeof raw === "function" ? raw() : raw;
92
+ inner = inner._def.innerType;
93
+ } else if (typeName === "ZodEffects") {
94
+ inner = inner._def.schema;
95
+ } else {
96
+ break;
97
+ }
98
+ if (!description && inner.description) {
99
+ description = inner.description;
100
+ }
101
+ }
102
+ return { inner, required, defaultValue, description };
103
+ }
104
+ function extractFieldMeta(key, zodType) {
105
+ const { inner, required, defaultValue, description } = unwrapType(zodType);
106
+ const typeName = resolveTypeName(inner);
107
+ let inputType;
108
+ let options = [];
109
+ let min;
110
+ let max;
111
+ let minLength;
112
+ let maxLength;
113
+ switch (typeName) {
114
+ case "ZodString": {
115
+ inputType = "text";
116
+ if (inner.format === "email") inputType = "email";
117
+ else if (inner.format === "url") inputType = "url";
118
+ if (inner.minLength != null) minLength = inner.minLength;
119
+ if (inner.maxLength != null) maxLength = inner.maxLength;
120
+ const checks = inner._def.checks ?? [];
121
+ for (const check of checks) {
122
+ const kind = check.kind ?? check.def?.check;
123
+ const fmt = check.format ?? check.def?.format;
124
+ if (!fmt && (kind === "email" || fmt === "email")) inputType = "email";
125
+ else if (!fmt && (kind === "url" || fmt === "url")) inputType = "url";
126
+ else if (fmt === "email" && inputType === "text") inputType = "email";
127
+ else if (fmt === "url" && inputType === "text") inputType = "url";
128
+ if (kind === "min" && check.value != null && minLength == null) minLength = check.value;
129
+ if (kind === "max" && check.value != null && maxLength == null) maxLength = check.value;
130
+ }
131
+ break;
132
+ }
133
+ case "ZodNumber": {
134
+ inputType = "number";
135
+ if (inner.minValue != null) min = inner.minValue;
136
+ if (inner.maxValue != null) max = inner.maxValue;
137
+ if (min == null || max == null) {
138
+ const checks = inner._def.checks ?? [];
139
+ for (const check of checks) {
140
+ if (check.kind === "min" && check.value != null && min == null) min = check.value;
141
+ if (check.kind === "max" && check.value != null && max == null) max = check.value;
142
+ }
143
+ }
144
+ break;
145
+ }
146
+ case "ZodBoolean":
147
+ inputType = "checkbox";
148
+ break;
149
+ case "ZodEnum": {
150
+ inputType = "select";
151
+ if (inner.options?.length) {
152
+ options = [...inner.options];
153
+ } else if (inner._def.entries) {
154
+ options = Object.values(inner._def.entries);
155
+ } else if (inner._def.values?.length) {
156
+ options = [...inner._def.values];
157
+ }
158
+ break;
159
+ }
160
+ case "ZodNativeEnum": {
161
+ inputType = "select";
162
+ const enumValues = inner._def.values;
163
+ if (enumValues) {
164
+ options = Object.values(enumValues).filter((v) => typeof v === "string");
165
+ }
166
+ break;
167
+ }
168
+ case "ZodDate":
169
+ inputType = "date";
170
+ break;
171
+ default:
172
+ return null;
173
+ }
174
+ const label = description || key;
175
+ return {
176
+ key,
177
+ inputType,
178
+ label,
179
+ description: description && description !== label ? description : "",
180
+ required,
181
+ options,
182
+ placeholder: "",
183
+ min,
184
+ max,
185
+ minLength,
186
+ maxLength,
187
+ defaultValue
188
+ };
189
+ }
190
+ function extractFormFields(schema) {
191
+ const shapeDef = schema._def.shape;
192
+ if (!shapeDef) return [];
193
+ const shape = typeof shapeDef === "function" ? shapeDef() : shapeDef;
194
+ if (!shape) return [];
195
+ const fields = [];
196
+ for (const [key, zodType] of Object.entries(shape)) {
197
+ const meta = extractFieldMeta(key, zodType);
198
+ if (meta) fields.push(meta);
199
+ }
200
+ return fields;
201
+ }
202
+
41
203
  // src/controller.ts
42
204
  function normalizeItemInput(input) {
43
205
  return typeof input === "string" ? { key: input, label: input } : input;
44
206
  }
45
- var _state, _listeners, _resolver, _timerId, _DialogController_instances, emit_fn, update_fn, createPromise_fn, resolve_fn, clearTimer_fn, updateItemStatus_fn;
207
+ 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;
46
208
  var DialogController = class {
47
209
  constructor() {
48
210
  __privateAdd(this, _DialogController_instances);
@@ -50,6 +212,10 @@ var DialogController = class {
50
212
  __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
51
213
  __privateAdd(this, _resolver, null);
52
214
  __privateAdd(this, _timerId, null);
215
+ __privateAdd(this, _formSchema, null);
216
+ __privateAdd(this, _formResult, null);
217
+ __privateAdd(this, _stepFormSchemas, []);
218
+ __privateAdd(this, _stepFormResults, {});
53
219
  __privateSet(this, _state, createInitialState());
54
220
  }
55
221
  // ─── Observable ──────────────────────────────────────────
@@ -195,9 +361,202 @@ var DialogController = class {
195
361
  clearSteps() {
196
362
  __privateMethod(this, _DialogController_instances, update_fn).call(this, { steps: [] });
197
363
  }
364
+ // ─── Form ─────────────────────────────────────────────────
365
+ form(schema, options) {
366
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
367
+ __privateSet(this, _formSchema, schema);
368
+ __privateSet(this, _formResult, null);
369
+ const fields = extractFormFields(schema);
370
+ const defaultValues = {};
371
+ for (const field of fields) {
372
+ if (field.defaultValue !== void 0) {
373
+ defaultValues[field.key] = field.defaultValue;
374
+ }
375
+ }
376
+ if (options?.defaultValues) {
377
+ Object.assign(defaultValues, options.defaultValues);
378
+ }
379
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
380
+ open: true,
381
+ dialogType: "form",
382
+ title: options?.title ?? "",
383
+ label: "",
384
+ description: options?.description ?? "",
385
+ icon: null,
386
+ showConfirmButton: true,
387
+ showCancelButton: true,
388
+ confirmButtonText: options?.confirmButtonText ?? "OK",
389
+ cancelButtonText: options?.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB",
390
+ allowOutsideClick: options?.allowOutsideClick ?? false,
391
+ allowEscapeKey: options?.allowEscapeKey ?? true,
392
+ progress: null,
393
+ timer: null,
394
+ formFields: fields,
395
+ formValues: defaultValues,
396
+ formErrors: {},
397
+ formTouched: {},
398
+ formLayout: options?.layout ?? {},
399
+ formValidateOnChange: options?.validateOnChange ?? true,
400
+ formValidateOnBlur: options?.validateOnBlur ?? true
401
+ });
402
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, null).then((r) => {
403
+ const data = __privateGet(this, _formResult);
404
+ __privateSet(this, _formSchema, null);
405
+ __privateSet(this, _formResult, null);
406
+ return r.isConfirmed ? data : null;
407
+ });
408
+ }
409
+ updateFormField(key, value) {
410
+ const formValues = { ...__privateGet(this, _state).formValues, [key]: value };
411
+ const formTouched = { ...__privateGet(this, _state).formTouched, [key]: true };
412
+ let formErrors = { ...__privateGet(this, _state).formErrors };
413
+ if (__privateGet(this, _formSchema) && __privateGet(this, _state).formValidateOnChange) {
414
+ formErrors = __privateMethod(this, _DialogController_instances, validateFormField_fn).call(this, key, formValues, formErrors);
415
+ }
416
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formValues, formTouched, formErrors });
417
+ }
418
+ touchFormField(key) {
419
+ const formTouched = { ...__privateGet(this, _state).formTouched, [key]: true };
420
+ let formErrors = { ...__privateGet(this, _state).formErrors };
421
+ if (__privateGet(this, _formSchema) && __privateGet(this, _state).formValidateOnBlur) {
422
+ formErrors = __privateMethod(this, _DialogController_instances, validateFormField_fn).call(this, key, __privateGet(this, _state).formValues, formErrors);
423
+ }
424
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formTouched, formErrors });
425
+ }
426
+ // ─── Step Form ───────────────────────────────────────────
427
+ showStepForm(steps, options) {
428
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
429
+ __privateSet(this, _stepFormSchemas, steps.map((s) => s.schema ?? null));
430
+ __privateSet(this, _stepFormResults, {});
431
+ const stepFormSteps = steps.map((s) => {
432
+ const fields = s.schema ? extractFormFields(s.schema) : [];
433
+ const values = {};
434
+ for (const f of fields) {
435
+ if (f.defaultValue !== void 0) values[f.key] = f.defaultValue;
436
+ }
437
+ if (s.defaultValues) Object.assign(values, s.defaultValues);
438
+ return {
439
+ key: s.key,
440
+ label: s.label,
441
+ description: s.description ?? "",
442
+ fields,
443
+ values,
444
+ errors: {},
445
+ touched: {},
446
+ layout: s.layout ?? {}
447
+ };
448
+ });
449
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
450
+ open: true,
451
+ dialogType: "step-form",
452
+ title: options?.title ?? "",
453
+ label: "",
454
+ description: "",
455
+ icon: null,
456
+ showConfirmButton: false,
457
+ showCancelButton: false,
458
+ allowOutsideClick: options?.allowOutsideClick ?? false,
459
+ allowEscapeKey: options?.allowEscapeKey ?? true,
460
+ progress: null,
461
+ timer: null,
462
+ stepFormSteps,
463
+ stepFormCurrentIndex: 0,
464
+ stepFormNextText: options?.nextButtonText ?? "\u6B21\u3078",
465
+ stepFormPrevText: options?.prevButtonText ?? "\u623B\u308B",
466
+ stepFormSubmitText: options?.submitButtonText ?? "OK",
467
+ cancelButtonText: options?.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB"
468
+ });
469
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, null).then((r) => {
470
+ const data = __privateGet(this, _stepFormResults);
471
+ __privateSet(this, _stepFormSchemas, []);
472
+ __privateSet(this, _stepFormResults, {});
473
+ return r.isConfirmed ? data : null;
474
+ });
475
+ }
476
+ onStepNext() {
477
+ const s = __privateGet(this, _state);
478
+ const idx = s.stepFormCurrentIndex;
479
+ const step = s.stepFormSteps[idx];
480
+ if (!step) return;
481
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
482
+ if (schema) {
483
+ const result = schema.safeParse(step.values);
484
+ if (!result.success) {
485
+ const errors = {};
486
+ for (const issue of result.error.issues) {
487
+ const key = issue.path[0]?.toString();
488
+ if (key && !errors[key]) errors[key] = issue.message;
489
+ }
490
+ const touched = {};
491
+ for (const f of step.fields) touched[f.key] = true;
492
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { errors, touched });
493
+ return;
494
+ }
495
+ __privateGet(this, _stepFormResults)[step.key] = result.data;
496
+ }
497
+ const isLast = idx === s.stepFormSteps.length - 1;
498
+ if (isLast) {
499
+ const r = { isConfirmed: true, isCanceled: false, isDismissed: false };
500
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
501
+ __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
502
+ return;
503
+ }
504
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormCurrentIndex: idx + 1 });
505
+ }
506
+ onStepPrev() {
507
+ const idx = __privateGet(this, _state).stepFormCurrentIndex;
508
+ if (idx <= 0) return;
509
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormCurrentIndex: idx - 1 });
510
+ }
511
+ updateStepFormField(fieldKey, value) {
512
+ const s = __privateGet(this, _state);
513
+ const idx = s.stepFormCurrentIndex;
514
+ const step = s.stepFormSteps[idx];
515
+ if (!step) return;
516
+ const values = { ...step.values, [fieldKey]: value };
517
+ const touched = { ...step.touched, [fieldKey]: true };
518
+ let errors = { ...step.errors };
519
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
520
+ if (schema && s.formValidateOnChange) {
521
+ errors = __privateMethod(this, _DialogController_instances, validateStepField_fn).call(this, schema, fieldKey, values, errors);
522
+ }
523
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { values, touched, errors });
524
+ }
525
+ touchStepFormField(fieldKey) {
526
+ const s = __privateGet(this, _state);
527
+ const idx = s.stepFormCurrentIndex;
528
+ const step = s.stepFormSteps[idx];
529
+ if (!step) return;
530
+ const touched = { ...step.touched, [fieldKey]: true };
531
+ let errors = { ...step.errors };
532
+ const schema = __privateGet(this, _stepFormSchemas)[idx];
533
+ if (schema && s.formValidateOnBlur) {
534
+ errors = __privateMethod(this, _DialogController_instances, validateStepField_fn).call(this, schema, fieldKey, step.values, errors);
535
+ }
536
+ __privateMethod(this, _DialogController_instances, updateCurrentStep_fn).call(this, { touched, errors });
537
+ }
198
538
  // ─── Button actions (called from the component) ──────────
199
539
  onConfirm() {
200
540
  __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
541
+ if (__privateGet(this, _state).dialogType === "form" && __privateGet(this, _formSchema)) {
542
+ const result = __privateGet(this, _formSchema).safeParse(__privateGet(this, _state).formValues);
543
+ if (!result.success) {
544
+ const formErrors = {};
545
+ for (const issue of result.error.issues) {
546
+ const key = issue.path[0]?.toString();
547
+ if (key && !formErrors[key]) {
548
+ formErrors[key] = issue.message;
549
+ }
550
+ }
551
+ const formTouched = {};
552
+ for (const field of __privateGet(this, _state).formFields) {
553
+ formTouched[field.key] = true;
554
+ }
555
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { formErrors, formTouched });
556
+ return;
557
+ }
558
+ __privateSet(this, _formResult, result.data);
559
+ }
201
560
  const r = { isConfirmed: true, isCanceled: false, isDismissed: false };
202
561
  __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
203
562
  __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
@@ -221,6 +580,10 @@ _state = new WeakMap();
221
580
  _listeners = new WeakMap();
222
581
  _resolver = new WeakMap();
223
582
  _timerId = new WeakMap();
583
+ _formSchema = new WeakMap();
584
+ _formResult = new WeakMap();
585
+ _stepFormSchemas = new WeakMap();
586
+ _stepFormResults = new WeakMap();
224
587
  _DialogController_instances = new WeakSet();
225
588
  emit_fn = function() {
226
589
  const snapshot = { ...__privateGet(this, _state) };
@@ -230,6 +593,47 @@ update_fn = function(patch) {
230
593
  Object.assign(__privateGet(this, _state), patch);
231
594
  __privateMethod(this, _DialogController_instances, emit_fn).call(this);
232
595
  };
596
+ validateFormField_fn = function(key, values, errors) {
597
+ const result = __privateGet(this, _formSchema).safeParse(values);
598
+ const updated = { ...errors };
599
+ if (result.success) {
600
+ delete updated[key];
601
+ } else {
602
+ const fieldIssue = result.error.issues.find(
603
+ (issue) => issue.path[0]?.toString() === key
604
+ );
605
+ if (fieldIssue) {
606
+ updated[key] = fieldIssue.message;
607
+ } else {
608
+ delete updated[key];
609
+ }
610
+ }
611
+ return updated;
612
+ };
613
+ updateCurrentStep_fn = function(patch) {
614
+ const idx = __privateGet(this, _state).stepFormCurrentIndex;
615
+ const stepFormSteps = __privateGet(this, _state).stepFormSteps.map(
616
+ (st, i) => i === idx ? { ...st, ...patch } : st
617
+ );
618
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { stepFormSteps });
619
+ };
620
+ validateStepField_fn = function(schema, fieldKey, values, errors) {
621
+ const result = schema.safeParse(values);
622
+ const updated = { ...errors };
623
+ if (result.success) {
624
+ delete updated[fieldKey];
625
+ } else {
626
+ const issue = result.error.issues.find(
627
+ (iss) => iss.path[0]?.toString() === fieldKey
628
+ );
629
+ if (issue) {
630
+ updated[fieldKey] = issue.message;
631
+ } else {
632
+ delete updated[fieldKey];
633
+ }
634
+ }
635
+ return updated;
636
+ };
233
637
  // ─── Internal ────────────────────────────────────────────
234
638
  createPromise_fn = function(timer) {
235
639
  return new Promise((resolve) => {
@@ -318,6 +722,24 @@ var overlayStyles = css`
318
722
  --dialog-spinner-track: rgb(59 130 246 / 0.2);
319
723
  --dialog-spinner-arc: var(--dialog-primary);
320
724
 
725
+ /* Form */
726
+ --dialog-form-width: 500px;
727
+ --dialog-form-max-height: 60vh;
728
+ --dialog-form-gap: 16px;
729
+ --dialog-form-columns: 1;
730
+ --dialog-form-label-color: #374151;
731
+ --dialog-form-label-size: 13px;
732
+ --dialog-form-label-weight: 500;
733
+ --dialog-form-input-bg: #fff;
734
+ --dialog-form-input-border: #d1d5db;
735
+ --dialog-form-input-border-focus: var(--dialog-primary);
736
+ --dialog-form-input-radius: 6px;
737
+ --dialog-form-input-padding: 8px 12px;
738
+ --dialog-form-input-font-size: 14px;
739
+ --dialog-form-error-color: var(--dialog-error);
740
+ --dialog-form-hint-color: #9ca3af;
741
+ --dialog-form-required-color: var(--dialog-error);
742
+
321
743
  display: contents;
322
744
  font-family: var(--dialog-font-family);
323
745
  color: var(--dialog-text-color);
@@ -469,8 +891,6 @@ var overlayStyles = css`
469
891
  }
470
892
  }
471
893
 
472
- /* spin と spinner-enter は同じ transform を書き換えるため衝突する。
473
- ラッパーでスケール/フェードを担い、.spinner は回転専用にする。 */
474
894
  .spinner-wrap {
475
895
  font-size: var(--dialog-spinner-size);
476
896
  width: 1em;
@@ -953,6 +1373,204 @@ var overlayStyles = css`
953
1373
  background-color: #e5e7eb;
954
1374
  transition: background-color 400ms ease;
955
1375
  }
1376
+
1377
+ /* ─── Form ─── */
1378
+
1379
+ @media (min-width: 640px) {
1380
+ .card[data-type='form'] {
1381
+ width: var(--dialog-form-width);
1382
+ }
1383
+ }
1384
+
1385
+ .form-scroll-container {
1386
+ max-height: var(--dialog-form-max-height);
1387
+ overflow-y: auto;
1388
+ width: 100%;
1389
+ padding: 4px 0;
1390
+ }
1391
+
1392
+ .form-grid {
1393
+ display: grid;
1394
+ grid-template-columns: repeat(var(--dialog-form-columns, 1), 1fr);
1395
+ gap: var(--dialog-form-gap);
1396
+ width: 100%;
1397
+ }
1398
+
1399
+ @media (max-width: 639px) {
1400
+ .form-grid {
1401
+ grid-template-columns: 1fr !important;
1402
+ }
1403
+ }
1404
+
1405
+ .form-field {
1406
+ display: flex;
1407
+ flex-direction: column;
1408
+ gap: 4px;
1409
+ text-align: left;
1410
+ }
1411
+
1412
+ .form-field[data-type='checkbox'] {
1413
+ grid-column: 1 / -1;
1414
+ }
1415
+
1416
+ .form-label {
1417
+ font-size: var(--dialog-form-label-size);
1418
+ font-weight: var(--dialog-form-label-weight);
1419
+ color: var(--dialog-form-label-color);
1420
+ }
1421
+
1422
+ .form-required {
1423
+ color: var(--dialog-form-required-color);
1424
+ margin-left: 2px;
1425
+ }
1426
+
1427
+ .form-input,
1428
+ .form-select {
1429
+ padding: var(--dialog-form-input-padding);
1430
+ font-size: var(--dialog-form-input-font-size);
1431
+ font-family: inherit;
1432
+ background: var(--dialog-form-input-bg);
1433
+ border: 1px solid var(--dialog-form-input-border);
1434
+ border-radius: var(--dialog-form-input-radius);
1435
+ color: var(--dialog-text-color);
1436
+ outline: none;
1437
+ transition:
1438
+ border-color 150ms ease,
1439
+ box-shadow 150ms ease;
1440
+ width: 100%;
1441
+ box-sizing: border-box;
1442
+ }
1443
+
1444
+ .form-input:focus,
1445
+ .form-select:focus {
1446
+ border-color: var(--dialog-form-input-border-focus);
1447
+ box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
1448
+ }
1449
+
1450
+ .form-field[data-error] .form-input,
1451
+ .form-field[data-error] .form-select {
1452
+ border-color: var(--dialog-form-error-color);
1453
+ }
1454
+
1455
+ .form-field[data-error] .form-input:focus,
1456
+ .form-field[data-error] .form-select:focus {
1457
+ box-shadow: 0 0 0 3px rgb(239 68 68 / 0.1);
1458
+ }
1459
+
1460
+ .form-error {
1461
+ font-size: 12px;
1462
+ color: var(--dialog-form-error-color);
1463
+ min-height: 1em;
1464
+ }
1465
+
1466
+ .form-hint {
1467
+ font-size: 12px;
1468
+ color: var(--dialog-form-hint-color);
1469
+ }
1470
+
1471
+ .form-checkbox {
1472
+ width: 18px;
1473
+ height: 18px;
1474
+ accent-color: var(--dialog-primary);
1475
+ cursor: pointer;
1476
+ flex-shrink: 0;
1477
+ }
1478
+
1479
+ .form-checkbox-label {
1480
+ display: flex;
1481
+ align-items: center;
1482
+ gap: 8px;
1483
+ cursor: pointer;
1484
+ font-size: var(--dialog-form-input-font-size);
1485
+ color: var(--dialog-form-label-color);
1486
+ }
1487
+
1488
+ .form-checkbox-text {
1489
+ font-size: var(--dialog-form-label-size);
1490
+ font-weight: var(--dialog-form-label-weight);
1491
+ color: var(--dialog-form-label-color);
1492
+ }
1493
+
1494
+ .form-group {
1495
+ border: 1px solid var(--dialog-card-border);
1496
+ border-radius: var(--dialog-form-input-radius);
1497
+ padding: 16px;
1498
+ margin: 0 0 8px;
1499
+ width: 100%;
1500
+ box-sizing: border-box;
1501
+ }
1502
+
1503
+ .form-group-label {
1504
+ font-size: var(--dialog-form-label-size);
1505
+ font-weight: 600;
1506
+ color: var(--dialog-form-label-color);
1507
+ padding: 0 4px;
1508
+ }
1509
+
1510
+ /* ─── Step Form ─── */
1511
+
1512
+ @media (min-width: 640px) {
1513
+ .card[data-type='step-form'] {
1514
+ width: var(--dialog-form-width);
1515
+ }
1516
+ }
1517
+
1518
+ .step-form-counter {
1519
+ font-size: 12px;
1520
+ color: var(--dialog-form-hint-color);
1521
+ text-align: center;
1522
+ margin: 0 0 4px;
1523
+ }
1524
+
1525
+ .actions-step-form {
1526
+ display: flex;
1527
+ flex-direction: row;
1528
+ justify-content: space-between;
1529
+ align-items: center;
1530
+ gap: 8px;
1531
+ margin-top: 20px;
1532
+ width: 100%;
1533
+ }
1534
+
1535
+ .step-form-nav {
1536
+ display: flex;
1537
+ flex-direction: row;
1538
+ gap: 8px;
1539
+ align-items: center;
1540
+ }
1541
+
1542
+ .btn-prev {
1543
+ padding: var(--dialog-btn-padding);
1544
+ font-size: var(--dialog-btn-font-size);
1545
+ font-family: inherit;
1546
+ font-weight: 500;
1547
+ border-radius: var(--dialog-btn-radius);
1548
+ cursor: pointer;
1549
+ border: 1px solid var(--dialog-form-input-border);
1550
+ background: transparent;
1551
+ color: var(--dialog-text-color);
1552
+ transition: background-color 120ms ease, border-color 120ms ease;
1553
+ }
1554
+
1555
+ .btn-prev:hover {
1556
+ background: #f9fafb;
1557
+ border-color: #9ca3af;
1558
+ }
1559
+
1560
+ .btn-prev:active {
1561
+ transform: scale(0.98);
1562
+ }
1563
+
1564
+ @media (max-width: 639px) {
1565
+ .actions-step-form {
1566
+ flex-direction: column-reverse;
1567
+ }
1568
+
1569
+ .step-form-nav {
1570
+ width: 100%;
1571
+ justify-content: flex-end;
1572
+ }
1573
+ }
956
1574
  `;
957
1575
 
958
1576
  // src/overlay-dialog.ts
@@ -976,6 +1594,7 @@ var OverlayDialog = class extends LitElement {
976
1594
  this._unsubscribe = this.controller.subscribe((s) => {
977
1595
  const wasOpen = this._state.open;
978
1596
  const prevDialogType = this._state.dialogType;
1597
+ const prevStepIndex = this._state.stepFormCurrentIndex;
979
1598
  if (s.open && !wasOpen) {
980
1599
  this._isClosing = false;
981
1600
  clearTimeout(this._closeTimer);
@@ -990,6 +1609,8 @@ var OverlayDialog = class extends LitElement {
990
1609
  }
991
1610
  if (s.open && s.dialogType !== prevDialogType) {
992
1611
  this._bodyKey++;
1612
+ } else if (s.open && s.dialogType === "step-form" && s.stepFormCurrentIndex !== prevStepIndex) {
1613
+ this._bodyKey++;
993
1614
  }
994
1615
  this._state = s;
995
1616
  this._syncBodyScroll(s.open);
@@ -1219,6 +1840,187 @@ var OverlayDialog = class extends LitElement {
1219
1840
  </ul>
1220
1841
  `;
1221
1842
  }
1843
+ // ─── Form Helpers ────────────────────────────────────────
1844
+ _createFormContext() {
1845
+ const s = this._state;
1846
+ return {
1847
+ getValue: (k) => s.formValues[k],
1848
+ getError: (k) => s.formErrors[k] ?? "",
1849
+ getTouched: (k) => !!s.formTouched[k],
1850
+ onUpdate: (k, v) => this.controller.updateFormField(k, v),
1851
+ onBlur: (k) => this.controller.touchFormField(k)
1852
+ };
1853
+ }
1854
+ _createStepFormContext() {
1855
+ const s = this._state;
1856
+ const step = s.stepFormSteps[s.stepFormCurrentIndex];
1857
+ return {
1858
+ getValue: (k) => step?.values[k],
1859
+ getError: (k) => step?.errors[k] ?? "",
1860
+ getTouched: (k) => !!step?.touched[k],
1861
+ onUpdate: (k, v) => this.controller.updateStepFormField(k, v),
1862
+ onBlur: (k) => this.controller.touchStepFormField(k)
1863
+ };
1864
+ }
1865
+ _getOrderedFields(fields, layout) {
1866
+ const order = layout.fieldOrder;
1867
+ if (!order?.length) return fields;
1868
+ const fieldMap = new Map(fields.map((f) => [f.key, f]));
1869
+ const ordered = [];
1870
+ for (const key of order) {
1871
+ const f = fieldMap.get(key);
1872
+ if (f) {
1873
+ ordered.push(f);
1874
+ fieldMap.delete(key);
1875
+ }
1876
+ }
1877
+ for (const f of fieldMap.values()) {
1878
+ ordered.push(f);
1879
+ }
1880
+ return ordered;
1881
+ }
1882
+ _renderFormGrid(fields, columns, gap, ctx) {
1883
+ return html`
1884
+ <div class="form-grid" style="--dialog-form-columns:${columns}; gap:${gap}">
1885
+ ${fields.map((f) => this._renderFormField(f, ctx))}
1886
+ </div>
1887
+ `;
1888
+ }
1889
+ _renderGroupedForm(allFields, groups, layout, ctx) {
1890
+ const fieldMap = new Map(allFields.map((f) => [f.key, f]));
1891
+ const usedKeys = /* @__PURE__ */ new Set();
1892
+ const gap = layout.gap ?? "16px";
1893
+ const groupFragments = groups.map((group) => {
1894
+ const groupFields = [];
1895
+ for (const key of group.fields) {
1896
+ const f = fieldMap.get(key);
1897
+ if (f) {
1898
+ groupFields.push(f);
1899
+ usedKeys.add(key);
1900
+ }
1901
+ }
1902
+ if (!groupFields.length) return nothing;
1903
+ const cols = group.columns ?? layout.columns ?? 1;
1904
+ return html`
1905
+ <fieldset class="form-group">
1906
+ ${group.label ? html`<legend class="form-group-label">${group.label}</legend>` : nothing}
1907
+ ${this._renderFormGrid(groupFields, cols, gap, ctx)}
1908
+ </fieldset>
1909
+ `;
1910
+ });
1911
+ const remaining = allFields.filter((f) => !usedKeys.has(f.key));
1912
+ return html`
1913
+ ${groupFragments}
1914
+ ${remaining.length ? this._renderFormGrid(remaining, layout.columns ?? 1, gap, ctx) : nothing}
1915
+ `;
1916
+ }
1917
+ _renderForm(fields, layout, ctx) {
1918
+ const ordered = this._getOrderedFields(fields, layout);
1919
+ const gap = layout.gap ?? "16px";
1920
+ if (layout.groups?.length) {
1921
+ return this._renderGroupedForm(ordered, layout.groups, layout, ctx);
1922
+ }
1923
+ return this._renderFormGrid(ordered, layout.columns ?? 1, gap, ctx);
1924
+ }
1925
+ _renderFormField(field, ctx) {
1926
+ const value = ctx.getValue(field.key);
1927
+ const error = ctx.getError(field.key);
1928
+ const touched = ctx.getTouched(field.key);
1929
+ const showError = touched && !!error;
1930
+ if (field.inputType === "checkbox") {
1931
+ return html`
1932
+ <div class="form-field" data-type="checkbox" ?data-error=${showError}>
1933
+ <label class="form-checkbox-label">
1934
+ <input
1935
+ type="checkbox"
1936
+ class="form-checkbox"
1937
+ .checked=${!!value}
1938
+ @change=${(e) => ctx.onUpdate(field.key, e.target.checked)}
1939
+ @blur=${() => ctx.onBlur(field.key)}
1940
+ />
1941
+ <span class="form-checkbox-text">
1942
+ ${field.label}
1943
+ ${field.required ? html`<span class="form-required">*</span>` : nothing}
1944
+ </span>
1945
+ </label>
1946
+ ${showError ? html`<span class="form-error">${error}</span>` : nothing}
1947
+ </div>
1948
+ `;
1949
+ }
1950
+ return html`
1951
+ <div class="form-field" data-type=${field.inputType} ?data-error=${showError}>
1952
+ <label class="form-label" for="form-${field.key}">
1953
+ ${field.label} ${field.required ? html`<span class="form-required">*</span>` : nothing}
1954
+ </label>
1955
+ ${field.description ? html`<span class="form-hint">${field.description}</span>` : nothing}
1956
+ ${this._renderFormInput(field, value, ctx)}
1957
+ ${showError ? html`<span class="form-error">${error}</span>` : nothing}
1958
+ </div>
1959
+ `;
1960
+ }
1961
+ _renderFormInput(field, value, ctx) {
1962
+ switch (field.inputType) {
1963
+ case "select":
1964
+ return html`
1965
+ <select
1966
+ class="form-select"
1967
+ id="form-${field.key}"
1968
+ @change=${(e) => ctx.onUpdate(field.key, e.target.value)}
1969
+ @blur=${() => ctx.onBlur(field.key)}
1970
+ >
1971
+ <option value="" ?selected=${!value}>選択してください</option>
1972
+ ${field.options.map(
1973
+ (opt) => html`<option value=${opt} ?selected=${value === opt}>${opt}</option>`
1974
+ )}
1975
+ </select>
1976
+ `;
1977
+ case "number":
1978
+ return html`
1979
+ <input
1980
+ type="number"
1981
+ class="form-input"
1982
+ id="form-${field.key}"
1983
+ .value=${value != null ? String(value) : ""}
1984
+ min=${field.min ?? nothing}
1985
+ max=${field.max ?? nothing}
1986
+ placeholder=${field.placeholder || nothing}
1987
+ @input=${(e) => {
1988
+ const v = e.target.valueAsNumber;
1989
+ ctx.onUpdate(field.key, Number.isNaN(v) ? void 0 : v);
1990
+ }}
1991
+ @blur=${() => ctx.onBlur(field.key)}
1992
+ />
1993
+ `;
1994
+ case "date":
1995
+ return html`
1996
+ <input
1997
+ type="date"
1998
+ class="form-input"
1999
+ id="form-${field.key}"
2000
+ .value=${value != null ? String(value) : ""}
2001
+ @input=${(e) => {
2002
+ const str = e.target.value;
2003
+ ctx.onUpdate(field.key, str || void 0);
2004
+ }}
2005
+ @blur=${() => ctx.onBlur(field.key)}
2006
+ />
2007
+ `;
2008
+ default:
2009
+ return html`
2010
+ <input
2011
+ type=${field.inputType}
2012
+ class="form-input"
2013
+ id="form-${field.key}"
2014
+ .value=${value ?? ""}
2015
+ minlength=${field.minLength ?? nothing}
2016
+ maxlength=${field.maxLength ?? nothing}
2017
+ placeholder=${field.placeholder || nothing}
2018
+ @input=${(e) => ctx.onUpdate(field.key, e.target.value)}
2019
+ @blur=${() => ctx.onBlur(field.key)}
2020
+ />
2021
+ `;
2022
+ }
2023
+ }
1222
2024
  _renderButtons() {
1223
2025
  const s = this._state;
1224
2026
  if (!s.showConfirmButton && !s.showCancelButton) return nothing;
@@ -1233,6 +2035,39 @@ var OverlayDialog = class extends LitElement {
1233
2035
  </div>
1234
2036
  `;
1235
2037
  }
2038
+ // ─── Step Form Helpers ───────────────────────────────────
2039
+ _renderStepFormButtons() {
2040
+ const s = this._state;
2041
+ const isFirst = s.stepFormCurrentIndex === 0;
2042
+ const isLast = s.stepFormCurrentIndex === s.stepFormSteps.length - 1;
2043
+ const submitText = isLast ? s.stepFormSubmitText : s.stepFormNextText;
2044
+ return html`
2045
+ <div class="actions actions-step-form">
2046
+ <button class="btn btn-cancel" @click=${() => this.controller.onCancel()}>
2047
+ ${s.cancelButtonText}
2048
+ </button>
2049
+ <div class="step-form-nav">
2050
+ ${isFirst ? nothing : html`
2051
+ <button class="btn btn-prev" @click=${() => this.controller.onStepPrev()}>
2052
+ ${s.stepFormPrevText}
2053
+ </button>
2054
+ `}
2055
+ <button class="btn btn-confirm" @click=${() => this.controller.onStepNext()}>
2056
+ ${submitText}
2057
+ </button>
2058
+ </div>
2059
+ </div>
2060
+ `;
2061
+ }
2062
+ _deriveStepItems() {
2063
+ const s = this._state;
2064
+ return s.stepFormSteps.map((st, i) => {
2065
+ let status = "pending";
2066
+ if (i < s.stepFormCurrentIndex) status = "done";
2067
+ else if (i === s.stepFormCurrentIndex) status = "active";
2068
+ return { key: st.key, label: st.label, status };
2069
+ });
2070
+ }
1236
2071
  _renderBody() {
1237
2072
  const s = this._state;
1238
2073
  switch (s.dialogType) {
@@ -1259,16 +2094,47 @@ var OverlayDialog = class extends LitElement {
1259
2094
  ${s.label ? html`<p class="label">${s.label}</p>` : nothing}
1260
2095
  ${this._renderStepsList(s.steps)}
1261
2096
  `;
2097
+ case "form": {
2098
+ const ctx = this._createFormContext();
2099
+ return html`
2100
+ ${s.title ? html`<p class="label">${s.title}</p>` : nothing}
2101
+ ${s.description ? html`<p class="description">${s.description}</p>` : nothing}
2102
+ <div class="form-scroll-container">
2103
+ ${this._renderForm(s.formFields, s.formLayout, ctx)}
2104
+ </div>
2105
+ ${this._renderButtons()}
2106
+ `;
2107
+ }
2108
+ case "step-form": {
2109
+ const stepItems = this._deriveStepItems();
2110
+ const currentStep = s.stepFormSteps[s.stepFormCurrentIndex];
2111
+ if (!currentStep) return html`${nothing}`;
2112
+ const ctx = this._createStepFormContext();
2113
+ const stepCount = s.stepFormSteps.length;
2114
+ const counterText = `${s.stepFormCurrentIndex + 1} / ${stepCount}`;
2115
+ return html`
2116
+ ${this._renderStepsHeader(stepItems)}
2117
+ <p class="step-form-counter">${counterText}</p>
2118
+ <p class="label">${currentStep.label}</p>
2119
+ ${currentStep.description ? html`<p class="description">${currentStep.description}</p>` : nothing}
2120
+ ${currentStep.fields.length ? html`
2121
+ <div class="form-scroll-container">
2122
+ ${this._renderForm(currentStep.fields, currentStep.layout, ctx)}
2123
+ </div>
2124
+ ` : nothing}
2125
+ ${this._renderStepFormButtons()}
2126
+ `;
2127
+ }
1262
2128
  default:
1263
2129
  return html`${nothing}`;
1264
2130
  }
1265
2131
  }
1266
2132
  render() {
1267
2133
  const s = this._state;
1268
- const showHeaderTitle = s.title && s.dialogType !== "alert" && s.dialogType !== "confirm";
2134
+ const showHeaderTitle = s.title && s.dialogType !== "alert" && s.dialogType !== "confirm" && s.dialogType !== "form" && s.dialogType !== "step-form";
1269
2135
  return html`
1270
2136
  <div class="backdrop" ?data-open=${s.open} @click=${this._onBackdropClick}>
1271
- <div class="card" ?data-closing=${this._isClosing}>
2137
+ <div class="card" data-type=${s.dialogType} ?data-closing=${this._isClosing}>
1272
2138
  ${showHeaderTitle ? html`<p class="dialog-title">${s.title}</p>` : nothing}
1273
2139
  <div class="card-body">
1274
2140
  ${keyed(this._bodyKey, html`<div class="body-inner">${this._renderBody()}</div>`)}
@@ -1324,6 +2190,28 @@ var DialogSingleton = class {
1324
2190
  __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1325
2191
  return __privateGet(this, _controller).confirm(optionsOrLabel);
1326
2192
  }
2193
+ // ─── Form ──────────────────────────────────────────────────
2194
+ form(schema, options) {
2195
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
2196
+ return __privateGet(this, _controller).form(schema, options);
2197
+ }
2198
+ // ─── Step Form ─────────────────────────────────────────────
2199
+ showStepForm(steps, options) {
2200
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
2201
+ return __privateGet(this, _controller).showStepForm(steps, options);
2202
+ }
2203
+ onStepNext() {
2204
+ __privateGet(this, _controller).onStepNext();
2205
+ }
2206
+ onStepPrev() {
2207
+ __privateGet(this, _controller).onStepPrev();
2208
+ }
2209
+ updateStepFormField(fieldKey, value) {
2210
+ __privateGet(this, _controller).updateStepFormField(fieldKey, value);
2211
+ }
2212
+ touchStepFormField(fieldKey) {
2213
+ __privateGet(this, _controller).touchStepFormField(fieldKey);
2214
+ }
1327
2215
  // ─── Loading helpers ─────────────────────────────────────
1328
2216
  showLoading(label) {
1329
2217
  __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
@@ -1404,9 +2292,908 @@ ensureElement_fn = function() {
1404
2292
  __privateSet(this, _element, el);
1405
2293
  };
1406
2294
  var dialog = new DialogSingleton();
2295
+
2296
+ // src/toast/types.ts
2297
+ var createInitialToastState = () => ({
2298
+ items: [],
2299
+ position: "top-right",
2300
+ maxVisible: 3,
2301
+ defaultDuration: 4e3
2302
+ });
2303
+
2304
+ // src/toast/controller.ts
2305
+ var DISMISS_ANIMATION_MS = 400;
2306
+ 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;
2307
+ var ToastController = class {
2308
+ constructor() {
2309
+ __privateAdd(this, _ToastController_instances);
2310
+ __privateAdd(this, _state2);
2311
+ __privateAdd(this, _listeners2, /* @__PURE__ */ new Set());
2312
+ // ─── Per-toast timer management ─────────────────────────
2313
+ __privateAdd(this, _timers, /* @__PURE__ */ new Map());
2314
+ __privateAdd(this, _timerStartedAt, /* @__PURE__ */ new Map());
2315
+ __privateAdd(this, _dismissTimers, /* @__PURE__ */ new Map());
2316
+ __privateAdd(this, _nextId, 0);
2317
+ __privateSet(this, _state2, createInitialToastState());
2318
+ }
2319
+ // ─── Observable ─────────────────────────────────────────
2320
+ get state() {
2321
+ return __privateGet(this, _state2);
2322
+ }
2323
+ subscribe(fn) {
2324
+ __privateGet(this, _listeners2).add(fn);
2325
+ return () => __privateGet(this, _listeners2).delete(fn);
2326
+ }
2327
+ // ─── Configuration ──────────────────────────────────────
2328
+ configure(config) {
2329
+ const patch = {};
2330
+ if (config.position != null) patch.position = config.position;
2331
+ if (config.maxVisible != null) patch.maxVisible = config.maxVisible;
2332
+ if (config.defaultDuration != null) patch.defaultDuration = config.defaultDuration;
2333
+ if (Object.keys(patch).length > 0) __privateMethod(this, _ToastController_instances, update_fn2).call(this, patch);
2334
+ }
2335
+ // ─── Show ───────────────────────────────────────────────
2336
+ show(options) {
2337
+ const id = `toast-${++__privateWrapper(this, _nextId)._}`;
2338
+ const duration = options.duration ?? __privateGet(this, _state2).defaultDuration;
2339
+ const item = {
2340
+ id,
2341
+ type: options.type ?? "info",
2342
+ message: options.message,
2343
+ description: options.description ?? "",
2344
+ action: options.action ?? null,
2345
+ duration,
2346
+ remainingMs: duration,
2347
+ paused: false,
2348
+ dismissing: false
2349
+ };
2350
+ const items = [...__privateGet(this, _state2).items, item];
2351
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2352
+ if (duration > 0) {
2353
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, duration);
2354
+ }
2355
+ __privateMethod(this, _ToastController_instances, enforceMaxVisible_fn).call(this);
2356
+ return id;
2357
+ }
2358
+ success(message, options) {
2359
+ return this.show({ ...options, type: "success", message });
2360
+ }
2361
+ error(message, options) {
2362
+ return this.show({ ...options, type: "error", message });
2363
+ }
2364
+ warning(message, options) {
2365
+ return this.show({ ...options, type: "warning", message });
2366
+ }
2367
+ info(message, options) {
2368
+ return this.show({ ...options, type: "info", message });
2369
+ }
2370
+ loading(message, options) {
2371
+ return this.show({ duration: 0, ...options, type: "loading", message });
2372
+ }
2373
+ // ─── Dismiss ────────────────────────────────────────────
2374
+ dismiss(id) {
2375
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2376
+ if (!item || item.dismissing) return;
2377
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, id);
2378
+ }
2379
+ dismissAll() {
2380
+ for (const item of __privateGet(this, _state2).items) {
2381
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, item.id);
2382
+ __privateGet(this, _timerStartedAt).delete(item.id);
2383
+ }
2384
+ const items = __privateGet(this, _state2).items.filter((i) => !i.dismissing).map((item) => ({ ...item, dismissing: true, paused: true }));
2385
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2386
+ const timer = setTimeout(() => {
2387
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items: [] });
2388
+ __privateGet(this, _dismissTimers).clear();
2389
+ }, DISMISS_ANIMATION_MS);
2390
+ __privateGet(this, _dismissTimers).set("__all__", timer);
2391
+ }
2392
+ // ─── Update ─────────────────────────────────────────────
2393
+ update(id, patch) {
2394
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2395
+ if (!item || item.dismissing) return;
2396
+ const { duration: newDuration, ...rest } = patch;
2397
+ const isLeavingLoading = item.type === "loading" && rest.type != null && rest.type !== "loading";
2398
+ if (newDuration !== void 0) {
2399
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2400
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { ...rest, duration: newDuration, remainingMs: newDuration, paused: false });
2401
+ if (newDuration > 0) {
2402
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, newDuration);
2403
+ }
2404
+ } else if (isLeavingLoading) {
2405
+ const autoMs = __privateGet(this, _state2).defaultDuration;
2406
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { ...rest, duration: autoMs, remainingMs: autoMs, paused: false });
2407
+ if (autoMs > 0) {
2408
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, autoMs);
2409
+ }
2410
+ } else {
2411
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, rest);
2412
+ }
2413
+ }
2414
+ // ─── Timer control (called from component) ─────────────
2415
+ pauseTimer(id) {
2416
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2417
+ if (!item || item.paused || item.dismissing || item.duration <= 0) return;
2418
+ const startedAt = __privateGet(this, _timerStartedAt).get(id);
2419
+ if (startedAt == null) return;
2420
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2421
+ const elapsed = Date.now() - startedAt;
2422
+ const remainingMs = Math.max(0, item.remainingMs - elapsed);
2423
+ __privateGet(this, _timerStartedAt).delete(id);
2424
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { paused: true, remainingMs });
2425
+ }
2426
+ resumeTimer(id) {
2427
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2428
+ if (!item || !item.paused || item.dismissing || item.duration <= 0) return;
2429
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { paused: false });
2430
+ const updated = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2431
+ if (updated && updated.remainingMs > 0) {
2432
+ __privateMethod(this, _ToastController_instances, startTimer_fn).call(this, id, updated.remainingMs);
2433
+ }
2434
+ }
2435
+ };
2436
+ _state2 = new WeakMap();
2437
+ _listeners2 = new WeakMap();
2438
+ _timers = new WeakMap();
2439
+ _timerStartedAt = new WeakMap();
2440
+ _dismissTimers = new WeakMap();
2441
+ _nextId = new WeakMap();
2442
+ _ToastController_instances = new WeakSet();
2443
+ emit_fn2 = function() {
2444
+ const snapshot = { ...__privateGet(this, _state2), items: [...__privateGet(this, _state2).items] };
2445
+ for (const fn of __privateGet(this, _listeners2)) fn(snapshot);
2446
+ };
2447
+ update_fn2 = function(patch) {
2448
+ Object.assign(__privateGet(this, _state2), patch);
2449
+ __privateMethod(this, _ToastController_instances, emit_fn2).call(this);
2450
+ };
2451
+ // ─── Internal ───────────────────────────────────────────
2452
+ startTimer_fn = function(id, durationMs) {
2453
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2454
+ __privateGet(this, _timerStartedAt).set(id, Date.now());
2455
+ __privateGet(this, _timers).set(
2456
+ id,
2457
+ setTimeout(() => {
2458
+ __privateGet(this, _timers).delete(id);
2459
+ __privateGet(this, _timerStartedAt).delete(id);
2460
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, id);
2461
+ }, durationMs)
2462
+ );
2463
+ };
2464
+ clearTimer_fn2 = function(id) {
2465
+ const timer = __privateGet(this, _timers).get(id);
2466
+ if (timer != null) {
2467
+ clearTimeout(timer);
2468
+ __privateGet(this, _timers).delete(id);
2469
+ }
2470
+ };
2471
+ beginDismiss_fn = function(id) {
2472
+ __privateMethod(this, _ToastController_instances, clearTimer_fn2).call(this, id);
2473
+ __privateGet(this, _timerStartedAt).delete(id);
2474
+ const item = __privateMethod(this, _ToastController_instances, findItem_fn).call(this, id);
2475
+ if (!item || item.dismissing) return;
2476
+ __privateMethod(this, _ToastController_instances, updateItem_fn).call(this, id, { dismissing: true, paused: true });
2477
+ __privateGet(this, _dismissTimers).set(
2478
+ id,
2479
+ setTimeout(() => {
2480
+ __privateMethod(this, _ToastController_instances, removeItem_fn).call(this, id);
2481
+ __privateGet(this, _dismissTimers).delete(id);
2482
+ }, DISMISS_ANIMATION_MS)
2483
+ );
2484
+ };
2485
+ removeItem_fn = function(id) {
2486
+ const items = __privateGet(this, _state2).items.filter((i) => i.id !== id);
2487
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2488
+ };
2489
+ updateItem_fn = function(id, patch) {
2490
+ const items = __privateGet(this, _state2).items.map((item) => item.id === id ? { ...item, ...patch } : item);
2491
+ __privateMethod(this, _ToastController_instances, update_fn2).call(this, { items });
2492
+ };
2493
+ findItem_fn = function(id) {
2494
+ return __privateGet(this, _state2).items.find((i) => i.id === id);
2495
+ };
2496
+ enforceMaxVisible_fn = function() {
2497
+ const active = __privateGet(this, _state2).items.filter((i) => !i.dismissing);
2498
+ const excess = active.length - __privateGet(this, _state2).maxVisible;
2499
+ if (excess <= 0) return;
2500
+ for (let i = 0; i < excess; i++) {
2501
+ __privateMethod(this, _ToastController_instances, beginDismiss_fn).call(this, active[i].id);
2502
+ }
2503
+ };
2504
+
2505
+ // src/toast/toast-container.ts
2506
+ import { LitElement as LitElement2, html as html2, nothing as nothing2 } from "lit";
2507
+ import { customElement as customElement2, property as property2, state as state2 } from "lit/decorators.js";
2508
+ import { repeat } from "lit/directives/repeat.js";
2509
+
2510
+ // src/toast/styles.ts
2511
+ import { css as css2 } from "lit";
2512
+ var toastStyles = css2`
2513
+ :host {
2514
+ /* ─── Customizable CSS Variables ─── */
2515
+ --toast-font-family: var(
2516
+ --dialog-font-family,
2517
+ 'Yu Gothic Medium',
2518
+ '游ゴシック',
2519
+ YuGothic,
2520
+ 'メイリオ',
2521
+ 'Hiragino Kaku Gothic ProN',
2522
+ Meiryo,
2523
+ sans-serif
2524
+ );
2525
+ --toast-text-color: var(--dialog-text-color, #356);
2526
+ --toast-z-index: 1100;
2527
+
2528
+ /* Container */
2529
+ --toast-gap: 12px;
2530
+ --toast-padding: 16px;
2531
+ --toast-max-width: 420px;
2532
+
2533
+ /* Card */
2534
+ --toast-card-bg: #fff;
2535
+ --toast-card-border: #f3f4f6;
2536
+ --toast-card-shadow: 0 4px 12px rgb(0 0 0 / 0.08), 0 1px 3px rgb(0 0 0 / 0.06);
2537
+ --toast-card-radius: 8px;
2538
+ --toast-card-padding: 14px 16px;
2539
+
2540
+ /* Colors (inherit from dialog when available) */
2541
+ --toast-success: var(--dialog-success, #22c55e);
2542
+ --toast-error: var(--dialog-error, #ef4444);
2543
+ --toast-warning: var(--dialog-warning, #f59e0b);
2544
+ --toast-info: var(--dialog-info, #3b82f6);
2545
+ --toast-loading: var(--dialog-primary, #3b82f6);
2546
+ --toast-spinner-track: rgb(59 130 246 / 0.2);
2547
+ --toast-spinner-arc: var(--toast-loading);
2548
+
2549
+ /* Progress bar */
2550
+ --toast-progress-height: 3px;
2551
+
2552
+ /* Close button */
2553
+ --toast-close-color: #9ca3af;
2554
+ --toast-close-hover-color: #374151;
2555
+
2556
+ /* Timing */
2557
+ --toast-enter-duration: 350ms;
2558
+ --toast-exit-duration: 200ms;
2559
+ --toast-collapse-duration: 200ms;
2560
+
2561
+ display: contents;
2562
+ font-family: var(--toast-font-family);
2563
+ color: var(--toast-text-color);
2564
+ }
2565
+
2566
+ /* ─── Container ─── */
2567
+
2568
+ .container {
2569
+ position: fixed;
2570
+ z-index: var(--toast-z-index);
2571
+ display: flex;
2572
+ flex-direction: column;
2573
+ gap: var(--toast-gap);
2574
+ padding: var(--toast-padding);
2575
+ pointer-events: none;
2576
+ max-height: 100vh;
2577
+ max-width: var(--toast-max-width);
2578
+ box-sizing: border-box;
2579
+ }
2580
+
2581
+ /* Position variants */
2582
+ .container[data-position='top-right'] {
2583
+ top: 0;
2584
+ right: 0;
2585
+ }
2586
+ .container[data-position='top-left'] {
2587
+ top: 0;
2588
+ left: 0;
2589
+ }
2590
+ .container[data-position='top-center'] {
2591
+ top: 0;
2592
+ left: 50%;
2593
+ transform: translateX(-50%);
2594
+ }
2595
+ .container[data-position='bottom-right'] {
2596
+ bottom: 0;
2597
+ right: 0;
2598
+ flex-direction: column-reverse;
2599
+ }
2600
+ .container[data-position='bottom-left'] {
2601
+ bottom: 0;
2602
+ left: 0;
2603
+ flex-direction: column-reverse;
2604
+ }
2605
+ .container[data-position='bottom-center'] {
2606
+ bottom: 0;
2607
+ left: 50%;
2608
+ transform: translateX(-50%);
2609
+ flex-direction: column-reverse;
2610
+ }
2611
+
2612
+ /* Responsive: full-width on mobile */
2613
+ @media (max-width: 639px) {
2614
+ .container {
2615
+ max-width: 100%;
2616
+ width: 100%;
2617
+ left: 0;
2618
+ right: 0;
2619
+ transform: none;
2620
+ }
2621
+ .container[data-position^='top'] {
2622
+ top: 0;
2623
+ flex-direction: column;
2624
+ }
2625
+ .container[data-position^='bottom'] {
2626
+ bottom: 0;
2627
+ flex-direction: column-reverse;
2628
+ }
2629
+ }
2630
+
2631
+ /* ─── Direction-based transform variables ─── */
2632
+
2633
+ .container[data-position$='right'] {
2634
+ --_enter-x: 110%;
2635
+ --_exit-x: 110%;
2636
+ --_enter-y: 0;
2637
+ --_exit-y: 0;
2638
+ }
2639
+ .container[data-position$='left'] {
2640
+ --_enter-x: -110%;
2641
+ --_exit-x: -110%;
2642
+ --_enter-y: 0;
2643
+ --_exit-y: 0;
2644
+ }
2645
+ .container[data-position='top-center'] {
2646
+ --_enter-x: 0;
2647
+ --_exit-x: 0;
2648
+ --_enter-y: -100%;
2649
+ --_exit-y: -100%;
2650
+ }
2651
+ .container[data-position='bottom-center'] {
2652
+ --_enter-x: 0;
2653
+ --_exit-x: 0;
2654
+ --_enter-y: 100%;
2655
+ --_exit-y: 100%;
2656
+ }
2657
+
2658
+ @media (max-width: 639px) {
2659
+ .container[data-position^='top'] {
2660
+ --_enter-x: 0;
2661
+ --_exit-x: 0;
2662
+ --_enter-y: -100%;
2663
+ --_exit-y: -100%;
2664
+ }
2665
+ .container[data-position^='bottom'] {
2666
+ --_enter-x: 0;
2667
+ --_exit-x: 0;
2668
+ --_enter-y: 100%;
2669
+ --_exit-y: 100%;
2670
+ }
2671
+ }
2672
+
2673
+ /* ─── Toast Slot (height-collapsing wrapper) ─── */
2674
+
2675
+ .toast-slot {
2676
+ display: grid;
2677
+ grid-template-rows: 1fr;
2678
+ transition: grid-template-rows var(--toast-collapse-duration) ease var(--toast-exit-duration);
2679
+ pointer-events: auto;
2680
+ }
2681
+
2682
+ .toast-slot[data-dismissing] {
2683
+ grid-template-rows: 0fr;
2684
+ }
2685
+
2686
+ .toast-slot > .toast-card {
2687
+ overflow: hidden;
2688
+ min-height: 0;
2689
+ }
2690
+
2691
+ /* ─── Toast Card ─── */
2692
+
2693
+ .toast-card {
2694
+ background: var(--toast-card-bg);
2695
+ border: 1px solid var(--toast-card-border);
2696
+ border-radius: var(--toast-card-radius);
2697
+ box-shadow: var(--toast-card-shadow);
2698
+ position: relative;
2699
+ overflow: hidden;
2700
+ animation: toast-enter var(--toast-enter-duration) cubic-bezier(0.16, 1, 0.3, 1) both;
2701
+ }
2702
+
2703
+ .toast-card[data-dismissing] {
2704
+ animation: toast-exit var(--toast-exit-duration) ease both;
2705
+ pointer-events: none;
2706
+ }
2707
+
2708
+ /* Type-based left accent */
2709
+ .toast-card[data-type='success'] {
2710
+ border-left: 3px solid var(--toast-success);
2711
+ }
2712
+ .toast-card[data-type='error'] {
2713
+ border-left: 3px solid var(--toast-error);
2714
+ }
2715
+ .toast-card[data-type='warning'] {
2716
+ border-left: 3px solid var(--toast-warning);
2717
+ }
2718
+ .toast-card[data-type='info'] {
2719
+ border-left: 3px solid var(--toast-info);
2720
+ }
2721
+ .toast-card[data-type='loading'] {
2722
+ border-left: 3px solid var(--toast-loading);
2723
+ }
2724
+
2725
+ /* ─── Animations ─── */
2726
+
2727
+ @keyframes toast-enter {
2728
+ from {
2729
+ opacity: 0;
2730
+ transform: translateX(var(--_enter-x, 0)) translateY(var(--_enter-y, 0));
2731
+ filter: blur(4px);
2732
+ }
2733
+ to {
2734
+ opacity: 1;
2735
+ transform: translateX(0) translateY(0);
2736
+ filter: blur(0);
2737
+ }
2738
+ }
2739
+
2740
+ @keyframes toast-exit {
2741
+ from {
2742
+ opacity: 1;
2743
+ transform: translateX(0) translateY(0);
2744
+ filter: blur(0);
2745
+ }
2746
+ to {
2747
+ opacity: 0;
2748
+ transform: translateX(var(--_exit-x, 0)) translateY(var(--_exit-y, 0));
2749
+ filter: blur(3px);
2750
+ }
2751
+ }
2752
+
2753
+ /* ─── Body ─── */
2754
+
2755
+ .toast-body {
2756
+ display: flex;
2757
+ align-items: flex-start;
2758
+ gap: 12px;
2759
+ padding: var(--toast-card-padding);
2760
+ }
2761
+
2762
+ /* ─── Icon ─── */
2763
+
2764
+ .toast-icon {
2765
+ width: 20px;
2766
+ height: 20px;
2767
+ flex-shrink: 0;
2768
+ margin-top: 1px;
2769
+ display: flex;
2770
+ align-items: center;
2771
+ justify-content: center;
2772
+ }
2773
+
2774
+ .toast-icon svg {
2775
+ width: 20px;
2776
+ height: 20px;
2777
+ }
2778
+
2779
+ .icon-success {
2780
+ color: var(--toast-success);
2781
+ }
2782
+ .icon-error {
2783
+ color: var(--toast-error);
2784
+ }
2785
+ .icon-warning {
2786
+ color: var(--toast-warning);
2787
+ }
2788
+ .icon-info {
2789
+ color: var(--toast-info);
2790
+ }
2791
+ .icon-loading {
2792
+ color: var(--toast-loading);
2793
+ }
2794
+
2795
+ /* ─── Loading Spinner ─── */
2796
+
2797
+ @keyframes toast-spin {
2798
+ to {
2799
+ transform: rotate(360deg);
2800
+ }
2801
+ }
2802
+
2803
+ .toast-spinner {
2804
+ width: 20px;
2805
+ height: 20px;
2806
+ border-radius: 50%;
2807
+ box-shadow: inset 0 0 0 2px var(--toast-spinner-track);
2808
+ position: relative;
2809
+ animation: toast-spin 1.2s infinite linear;
2810
+ flex-shrink: 0;
2811
+ }
2812
+
2813
+ .toast-spinner-half {
2814
+ position: absolute;
2815
+ left: 50%;
2816
+ top: 50%;
2817
+ width: 10px;
2818
+ height: 20px;
2819
+ margin-left: -10px;
2820
+ margin-top: -10px;
2821
+ overflow: hidden;
2822
+ transform-origin: 10px 10px;
2823
+ mask-image: linear-gradient(to bottom, #000f, #0000);
2824
+ -webkit-mask-image: linear-gradient(to bottom, #000f, #0000);
2825
+ }
2826
+
2827
+ .toast-spinner-inner {
2828
+ width: 20px;
2829
+ height: 20px;
2830
+ border-radius: 50%;
2831
+ box-shadow: inset 0 0 0 2px var(--toast-spinner-arc);
2832
+ }
2833
+
2834
+ /* ─── Text ─── */
2835
+
2836
+ .toast-text {
2837
+ flex: 1;
2838
+ min-width: 0;
2839
+ display: flex;
2840
+ flex-direction: column;
2841
+ gap: 4px;
2842
+ }
2843
+
2844
+ .toast-message {
2845
+ font-size: 14px;
2846
+ font-weight: 500;
2847
+ color: #1f2937;
2848
+ margin: 0;
2849
+ word-break: break-word;
2850
+ line-height: 1.4;
2851
+ }
2852
+
2853
+ .toast-description {
2854
+ font-size: 13px;
2855
+ color: #6b7280;
2856
+ margin: 0;
2857
+ word-break: break-word;
2858
+ line-height: 1.5;
2859
+ }
2860
+
2861
+ /* ─── Action Button ─── */
2862
+
2863
+ .toast-action-btn {
2864
+ background: none;
2865
+ border: none;
2866
+ padding: 0;
2867
+ font-family: inherit;
2868
+ font-size: 13px;
2869
+ font-weight: 600;
2870
+ color: var(--toast-info);
2871
+ cursor: pointer;
2872
+ align-self: flex-start;
2873
+ text-decoration: underline;
2874
+ text-underline-offset: 2px;
2875
+ transition: color 150ms ease;
2876
+ }
2877
+
2878
+ .toast-action-btn:hover {
2879
+ color: var(--dialog-primary-hover, #2563eb);
2880
+ }
2881
+
2882
+ /* ─── Close Button ─── */
2883
+
2884
+ .toast-close {
2885
+ width: 20px;
2886
+ height: 20px;
2887
+ padding: 0;
2888
+ margin: 0;
2889
+ border: none;
2890
+ background: none;
2891
+ cursor: pointer;
2892
+ flex-shrink: 0;
2893
+ display: flex;
2894
+ align-items: center;
2895
+ justify-content: center;
2896
+ color: var(--toast-close-color);
2897
+ transition: color 150ms ease;
2898
+ border-radius: 4px;
2899
+ }
2900
+
2901
+ .toast-close svg {
2902
+ width: 14px;
2903
+ height: 14px;
2904
+ }
2905
+
2906
+ .toast-close:hover {
2907
+ color: var(--toast-close-hover-color);
2908
+ }
2909
+
2910
+ @media (min-width: 640px) {
2911
+ .toast-close {
2912
+ opacity: 0.5;
2913
+ transition:
2914
+ opacity 150ms ease,
2915
+ color 150ms ease;
2916
+ }
2917
+ .toast-card:hover .toast-close {
2918
+ opacity: 1;
2919
+ }
2920
+ }
2921
+
2922
+ /* ─── Progress Bar ─── */
2923
+
2924
+ .toast-progress {
2925
+ position: absolute;
2926
+ bottom: 0;
2927
+ left: 0;
2928
+ height: var(--toast-progress-height);
2929
+ width: 100%;
2930
+ border-radius: 0 0 var(--toast-card-radius) var(--toast-card-radius);
2931
+ animation: toast-progress-countdown linear forwards;
2932
+ animation-play-state: running;
2933
+ }
2934
+
2935
+ .toast-card[data-paused] .toast-progress {
2936
+ animation-play-state: paused;
2937
+ }
2938
+
2939
+ @keyframes toast-progress-countdown {
2940
+ from {
2941
+ width: 100%;
2942
+ }
2943
+ to {
2944
+ width: 0%;
2945
+ }
2946
+ }
2947
+
2948
+ .toast-progress[data-type='success'] {
2949
+ background-color: var(--toast-success);
2950
+ }
2951
+ .toast-progress[data-type='error'] {
2952
+ background-color: var(--toast-error);
2953
+ }
2954
+ .toast-progress[data-type='warning'] {
2955
+ background-color: var(--toast-warning);
2956
+ }
2957
+ .toast-progress[data-type='info'] {
2958
+ background-color: var(--toast-info);
2959
+ }
2960
+ `;
2961
+
2962
+ // src/toast/toast-container.ts
2963
+ var ToastContainer = class extends LitElement2 {
2964
+ constructor() {
2965
+ super(...arguments);
2966
+ this._state = createInitialToastState();
2967
+ }
2968
+ connectedCallback() {
2969
+ super.connectedCallback();
2970
+ if (this.controller) {
2971
+ this._state = { ...this.controller.state };
2972
+ this._unsubscribe = this.controller.subscribe((s) => {
2973
+ this._state = s;
2974
+ });
2975
+ }
2976
+ }
2977
+ disconnectedCallback() {
2978
+ super.disconnectedCallback();
2979
+ this._unsubscribe?.();
2980
+ }
2981
+ // ─── Icon Rendering ─────────────────────────────────────
2982
+ _renderIcon(type) {
2983
+ switch (type) {
2984
+ case "success":
2985
+ return html2`<svg
2986
+ viewBox="0 0 24 24"
2987
+ fill="none"
2988
+ stroke="currentColor"
2989
+ stroke-width="2"
2990
+ stroke-linecap="round"
2991
+ stroke-linejoin="round"
2992
+ >
2993
+ <circle cx="12" cy="12" r="10" />
2994
+ <polyline points="9,12 11,14 15,10" />
2995
+ </svg>`;
2996
+ case "error":
2997
+ return html2`<svg
2998
+ viewBox="0 0 24 24"
2999
+ fill="none"
3000
+ stroke="currentColor"
3001
+ stroke-width="2"
3002
+ stroke-linecap="round"
3003
+ stroke-linejoin="round"
3004
+ >
3005
+ <circle cx="12" cy="12" r="10" />
3006
+ <line x1="15" y1="9" x2="9" y2="15" />
3007
+ <line x1="9" y1="9" x2="15" y2="15" />
3008
+ </svg>`;
3009
+ case "warning":
3010
+ return html2`<svg
3011
+ viewBox="0 0 24 24"
3012
+ fill="none"
3013
+ stroke="currentColor"
3014
+ stroke-width="2"
3015
+ stroke-linecap="round"
3016
+ stroke-linejoin="round"
3017
+ >
3018
+ <path
3019
+ 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"
3020
+ />
3021
+ <line x1="12" y1="9" x2="12" y2="13" />
3022
+ <line x1="12" y1="17" x2="12.01" y2="17" />
3023
+ </svg>`;
3024
+ case "info":
3025
+ return html2`<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="12" y1="16" x2="12" y2="12" />
3035
+ <line x1="12" y1="8" x2="12.01" y2="8" />
3036
+ </svg>`;
3037
+ case "loading":
3038
+ return html2`
3039
+ <div class="toast-spinner">
3040
+ <div class="toast-spinner-half">
3041
+ <div class="toast-spinner-inner"></div>
3042
+ </div>
3043
+ </div>
3044
+ `;
3045
+ }
3046
+ }
3047
+ // ─── Toast Rendering ────────────────────────────────────
3048
+ _renderToast(item) {
3049
+ return html2`
3050
+ <div class="toast-slot" ?data-dismissing=${item.dismissing}>
3051
+ <div
3052
+ class="toast-card"
3053
+ data-type=${item.type}
3054
+ ?data-dismissing=${item.dismissing}
3055
+ ?data-paused=${item.paused}
3056
+ @mouseenter=${() => this.controller.pauseTimer(item.id)}
3057
+ @mouseleave=${() => this.controller.resumeTimer(item.id)}
3058
+ >
3059
+ <div class="toast-body">
3060
+ <span class="toast-icon icon-${item.type}"> ${this._renderIcon(item.type)} </span>
3061
+ <div class="toast-text">
3062
+ <p class="toast-message">${item.message}</p>
3063
+ ${item.description ? html2`<p class="toast-description">${item.description}</p>` : nothing2}
3064
+ ${item.action ? html2`<button
3065
+ class="toast-action-btn"
3066
+ @click=${() => {
3067
+ item.action.onClick();
3068
+ this.controller.dismiss(item.id);
3069
+ }}
3070
+ >
3071
+ ${item.action.label}
3072
+ </button>` : nothing2}
3073
+ </div>
3074
+ <button
3075
+ class="toast-close"
3076
+ @click=${() => this.controller.dismiss(item.id)}
3077
+ aria-label="閉じる"
3078
+ >
3079
+ <svg
3080
+ viewBox="0 0 24 24"
3081
+ fill="none"
3082
+ stroke="currentColor"
3083
+ stroke-width="2"
3084
+ stroke-linecap="round"
3085
+ stroke-linejoin="round"
3086
+ >
3087
+ <line x1="18" y1="6" x2="6" y2="18" />
3088
+ <line x1="6" y1="6" x2="18" y2="18" />
3089
+ </svg>
3090
+ </button>
3091
+ </div>
3092
+ ${item.duration > 0 && item.type !== "loading" ? html2`<div
3093
+ class="toast-progress"
3094
+ style="animation-duration:${item.duration}ms"
3095
+ data-type=${item.type}
3096
+ ></div>` : nothing2}
3097
+ </div>
3098
+ </div>
3099
+ `;
3100
+ }
3101
+ // ─── Main Render ────────────────────────────────────────
3102
+ render() {
3103
+ const s = this._state;
3104
+ return html2`
3105
+ <div class="container" data-position=${s.position} role="region" aria-label="通知">
3106
+ ${repeat(
3107
+ s.items,
3108
+ (item) => item.id,
3109
+ (item) => this._renderToast(item)
3110
+ )}
3111
+ </div>
3112
+ `;
3113
+ }
3114
+ };
3115
+ ToastContainer.styles = toastStyles;
3116
+ __decorateClass([
3117
+ property2({ attribute: false })
3118
+ ], ToastContainer.prototype, "controller", 2);
3119
+ __decorateClass([
3120
+ state2()
3121
+ ], ToastContainer.prototype, "_state", 2);
3122
+ ToastContainer = __decorateClass([
3123
+ customElement2("toast-container")
3124
+ ], ToastContainer);
3125
+
3126
+ // src/toast/toast.ts
3127
+ var _controller2, _element2, _ToastSingleton_instances, ensureElement_fn2;
3128
+ var ToastSingleton = class {
3129
+ constructor() {
3130
+ __privateAdd(this, _ToastSingleton_instances);
3131
+ __privateAdd(this, _controller2, new ToastController());
3132
+ __privateAdd(this, _element2, null);
3133
+ }
3134
+ // ─── Configuration ──────────────────────────────────────
3135
+ configure(config) {
3136
+ __privateGet(this, _controller2).configure(config);
3137
+ }
3138
+ // ─── Show Methods ───────────────────────────────────────
3139
+ show(options) {
3140
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3141
+ return __privateGet(this, _controller2).show(options);
3142
+ }
3143
+ success(message, options) {
3144
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3145
+ return __privateGet(this, _controller2).success(message, options);
3146
+ }
3147
+ error(message, options) {
3148
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3149
+ return __privateGet(this, _controller2).error(message, options);
3150
+ }
3151
+ warning(message, options) {
3152
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3153
+ return __privateGet(this, _controller2).warning(message, options);
3154
+ }
3155
+ info(message, options) {
3156
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3157
+ return __privateGet(this, _controller2).info(message, options);
3158
+ }
3159
+ loading(message, options) {
3160
+ __privateMethod(this, _ToastSingleton_instances, ensureElement_fn2).call(this);
3161
+ return __privateGet(this, _controller2).loading(message, options);
3162
+ }
3163
+ // ─── Dismiss ────────────────────────────────────────────
3164
+ dismiss(id) {
3165
+ __privateGet(this, _controller2).dismiss(id);
3166
+ }
3167
+ dismissAll() {
3168
+ __privateGet(this, _controller2).dismissAll();
3169
+ }
3170
+ // ─── Update ─────────────────────────────────────────────
3171
+ update(id, patch) {
3172
+ __privateGet(this, _controller2).update(id, patch);
3173
+ }
3174
+ // ─── Advanced ───────────────────────────────────────────
3175
+ get controller() {
3176
+ return __privateGet(this, _controller2);
3177
+ }
3178
+ };
3179
+ _controller2 = new WeakMap();
3180
+ _element2 = new WeakMap();
3181
+ _ToastSingleton_instances = new WeakSet();
3182
+ ensureElement_fn2 = function() {
3183
+ if (__privateGet(this, _element2)) return;
3184
+ if (typeof document === "undefined") return;
3185
+ const el = document.createElement("toast-container");
3186
+ el.controller = __privateGet(this, _controller2);
3187
+ document.body.appendChild(el);
3188
+ __privateSet(this, _element2, el);
3189
+ };
3190
+ var toast = new ToastSingleton();
1407
3191
  export {
1408
3192
  DialogController,
1409
3193
  OverlayDialog,
1410
- dialog
3194
+ ToastContainer,
3195
+ ToastController,
3196
+ dialog,
3197
+ toast
1411
3198
  };
1412
3199
  //# sourceMappingURL=index.js.map