@konomi-app/ui 3.0.0 → 4.0.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
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __typeError = (msg) => {
7
+ throw TypeError(msg);
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,165 +19,399 @@ var __copyProps = (to, from, except, desc) => {
16
19
  return to;
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var __decorateClass = (decorators, target, key, kind) => {
23
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
24
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
25
+ if (decorator = decorators[i])
26
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
27
+ if (kind && result) __defProp(target, key, result);
28
+ return result;
29
+ };
30
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
31
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
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
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
34
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
19
35
 
20
36
  // src/index.ts
21
37
  var index_exports = {};
22
38
  __export(index_exports, {
23
- ATTRIBUTE_ANIMATION: () => ATTRIBUTE_ANIMATION,
24
- LoadingOverlay: () => LoadingOverlay,
25
- Modal: () => Modal,
26
- TaskListOverlay: () => TaskListOverlay,
27
- withLoading: () => withLoading
39
+ DialogController: () => DialogController,
40
+ OverlayDialog: () => OverlayDialog,
41
+ dialog: () => dialog
28
42
  });
29
43
  module.exports = __toCommonJS(index_exports);
30
44
 
31
- // src/overlay/style.ts
32
- var import_css = require("@emotion/css");
33
- var getBodyStyle = () => import_css.css`
34
- overflow: visible;
35
- &[${ATTRIBUTE_KEY}] {
36
- overflow: hidden;
45
+ // src/types.ts
46
+ var createInitialState = () => ({
47
+ open: false,
48
+ dialogType: "loading",
49
+ icon: null,
50
+ label: "",
51
+ description: "",
52
+ html: "",
53
+ progress: null,
54
+ showConfirmButton: true,
55
+ showCancelButton: false,
56
+ confirmButtonText: "OK",
57
+ cancelButtonText: "\u30AD\u30E3\u30F3\u30BB\u30EB",
58
+ allowOutsideClick: true,
59
+ allowEscapeKey: true,
60
+ queues: [],
61
+ steps: [],
62
+ timer: null
63
+ });
64
+
65
+ // src/controller.ts
66
+ var _state, _listeners, _resolver, _timerId, _DialogController_instances, emit_fn, update_fn, createPromise_fn, resolve_fn, clearTimer_fn, updateItemStatus_fn;
67
+ var DialogController = class {
68
+ constructor() {
69
+ __privateAdd(this, _DialogController_instances);
70
+ __privateAdd(this, _state);
71
+ __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
72
+ __privateAdd(this, _resolver, null);
73
+ __privateAdd(this, _timerId, null);
74
+ __privateSet(this, _state, createInitialState());
37
75
  }
38
- `;
39
- var getRootStyle = () => import_css.css`
40
- font-family: 'Yu Gothic Medium', '游ゴシック', YuGothic, 'メイリオ', 'Hiragino Kaku Gothic ProN',
41
- Meiryo, sans-serif;
42
- color: #356;
43
- font-size: 14px;
44
-
45
- overflow: hidden;
46
- background-color: #fffb;
47
- backdrop-filter: blur(4px);
48
- box-sizing: content-box;
49
-
50
- position: fixed;
51
- inset: 0;
52
- width: 100vw;
53
- height: 100vh;
54
-
55
- display: grid;
56
- transition: all 250ms ease;
57
- z-index: 1000;
58
- opacity: 0;
59
- transition: all 250ms ease;
60
-
61
- place-items: end stretch;
62
- @media (min-width: 640px) {
63
- font-size: 16px;
64
- place-items: center;
76
+ // ─── Observable ──────────────────────────────────────────
77
+ get state() {
78
+ return __privateGet(this, _state);
65
79
  }
66
-
67
- opacity: 0;
68
- pointer-events: none;
69
- &[${ATTRIBUTE_SHOWN}] {
70
- opacity: 1;
71
- pointer-events: all;
80
+ subscribe(fn) {
81
+ __privateGet(this, _listeners).add(fn);
82
+ return () => __privateGet(this, _listeners).delete(fn);
72
83
  }
73
- `;
74
-
75
- // src/overlay/index.ts
76
- var ATTRIBUTE_KEY = "data-konomi-ui-overlay";
77
- var ATTRIBUTE_SHOWN = "data-shown";
78
- var Overlay = class {
79
- /**
80
- * オーバーレイをレンダリングするルート要素。
81
- */
82
- #root;
83
- /**
84
- * オーバーレイをレンダリングするルート要素の`dataset`。
85
- */
86
- _rootDataset;
87
- /**
88
- * オーバーレイが表示されているかどうか。
89
- */
90
- _shown;
91
- /**
92
- * オーバーレイを表示中にページを離れようとした場合にアラートを表示するかどうかを設定します。
93
- */
94
- _disableBeforeUnload;
95
- constructor() {
96
- this._shown = false;
97
- this._disableBeforeUnload = false;
98
- this._rootDataset = {};
99
- const root = document.createElement("div");
100
- this.#root = root;
101
- this.#root.classList.add(getRootStyle());
102
- document.body.classList.add(getBodyStyle());
103
- this.#root.setAttribute(ATTRIBUTE_KEY, "");
104
- document.body.append(root);
105
- }
106
- show() {
107
- this._shown = true;
108
- this.#root.setAttribute(ATTRIBUTE_SHOWN, "");
109
- document.body.setAttribute(ATTRIBUTE_KEY, "");
110
- window.addEventListener("beforeunload", this.beforeunload);
111
- this.render();
84
+ // ─── Core ────────────────────────────────────────────────
85
+ show(options = { type: "loading" }) {
86
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
87
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
88
+ open: true,
89
+ dialogType: options.type,
90
+ label: options.label ?? "",
91
+ description: options.description ?? "",
92
+ icon: options.icon ?? null,
93
+ progress: options.progress ?? null,
94
+ allowOutsideClick: options.allowOutsideClick ?? false,
95
+ showConfirmButton: false,
96
+ showCancelButton: false
97
+ });
112
98
  }
113
99
  hide() {
114
- this._shown = false;
115
- this.#root.removeAttribute(ATTRIBUTE_SHOWN);
116
- document.body.removeAttribute(ATTRIBUTE_KEY);
117
- window.removeEventListener("beforeunload", this.beforeunload);
118
- this.render();
100
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
101
+ const wasOpen = __privateGet(this, _state).open;
102
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
103
+ if (wasOpen && __privateGet(this, _resolver)) {
104
+ __privateMethod(this, _DialogController_instances, resolve_fn).call(this, { isConfirmed: false, isCanceled: false, isDismissed: true });
105
+ }
119
106
  }
120
- render() {
107
+ // ─── Alert ───────────────────────────────────────────────
108
+ alert(optionsOrLabel) {
109
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
110
+ const opts = typeof optionsOrLabel === "string" ? { label: optionsOrLabel } : optionsOrLabel;
111
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
112
+ open: true,
113
+ dialogType: "alert",
114
+ icon: opts.type ?? "info",
115
+ label: opts.label ?? "",
116
+ description: opts.description ?? "",
117
+ html: opts.html ?? "",
118
+ showConfirmButton: true,
119
+ showCancelButton: opts.showCancelButton ?? false,
120
+ confirmButtonText: opts.confirmButtonText ?? "OK",
121
+ cancelButtonText: opts.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB",
122
+ allowOutsideClick: opts.allowOutsideClick ?? true,
123
+ allowEscapeKey: opts.allowEscapeKey ?? true,
124
+ progress: null,
125
+ timer: opts.timer ?? null
126
+ });
127
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, opts.timer ?? null);
128
+ }
129
+ // ─── Confirm ─────────────────────────────────────────────
130
+ confirm(optionsOrLabel) {
131
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
132
+ const opts = typeof optionsOrLabel === "string" ? { label: optionsOrLabel } : optionsOrLabel;
133
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
134
+ open: true,
135
+ dialogType: "confirm",
136
+ icon: opts.type ?? "warning",
137
+ label: opts.label ?? "",
138
+ description: opts.description ?? "",
139
+ showConfirmButton: true,
140
+ showCancelButton: true,
141
+ confirmButtonText: opts.confirmButtonText ?? "OK",
142
+ cancelButtonText: opts.cancelButtonText ?? "\u30AD\u30E3\u30F3\u30BB\u30EB",
143
+ allowOutsideClick: opts.allowOutsideClick ?? false,
144
+ allowEscapeKey: opts.allowEscapeKey ?? true,
145
+ progress: null,
146
+ timer: null
147
+ });
148
+ return __privateMethod(this, _DialogController_instances, createPromise_fn).call(this, null).then((r) => r.isConfirmed);
149
+ }
150
+ // ─── Loading helpers ─────────────────────────────────────
151
+ loading(label) {
152
+ this.show({ type: "loading", label });
153
+ }
154
+ progress(percent) {
155
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { progress: percent });
156
+ }
157
+ label(label) {
158
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { label });
159
+ }
160
+ description(description) {
161
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { description });
162
+ }
163
+ // ─── Queue ───────────────────────────────────────────────
164
+ setQueues(labels) {
165
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
166
+ queues: labels.map((label) => ({ label, status: "pending" }))
167
+ });
168
+ }
169
+ queue(labels) {
170
+ this.setQueues(labels);
171
+ this.show({ type: "queue" });
172
+ }
173
+ startQueue(label) {
174
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "queues", label, "active");
175
+ }
176
+ finishQueue(label) {
177
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "queues", label, "done");
178
+ }
179
+ skipQueue(label) {
180
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "queues", label, "skipped");
181
+ }
182
+ errorQueue(label) {
183
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "queues", label, "error");
184
+ }
185
+ clearQueues() {
186
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { queues: [] });
187
+ }
188
+ // ─── Steps ───────────────────────────────────────────────
189
+ setSteps(labels) {
190
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, {
191
+ steps: labels.map((label) => ({ label, status: "pending" }))
192
+ });
193
+ }
194
+ steps(labels) {
195
+ this.setSteps(labels);
196
+ this.show({ type: "steps" });
197
+ }
198
+ startStep(label) {
199
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "steps", label, "active");
200
+ }
201
+ finishStep(label) {
202
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "steps", label, "done");
203
+ }
204
+ skipStep(label) {
205
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "steps", label, "skipped");
206
+ }
207
+ errorStep(label) {
208
+ __privateMethod(this, _DialogController_instances, updateItemStatus_fn).call(this, "steps", label, "error");
209
+ }
210
+ clearSteps() {
211
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { steps: [] });
121
212
  }
122
- /** JavaScript中にページを離れようとした場合にアラートを表示します */
123
- beforeunload(event) {
124
- event.preventDefault();
213
+ // ─── Button actions (called from the component) ──────────
214
+ onConfirm() {
215
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
216
+ const r = { isConfirmed: true, isCanceled: false, isDismissed: false };
217
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
218
+ __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
125
219
  }
126
- get root() {
127
- return this.#root;
220
+ onCancel() {
221
+ __privateMethod(this, _DialogController_instances, clearTimer_fn).call(this);
222
+ const r = { isConfirmed: false, isCanceled: true, isDismissed: false };
223
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
224
+ __privateMethod(this, _DialogController_instances, resolve_fn).call(this, r);
128
225
  }
129
- /** @deprecated このメソッドは非推奨です。代わりに`show`メソッドを使用してください。 */
130
- start() {
131
- this.show();
226
+ onOutsideClick() {
227
+ if (!__privateGet(this, _state).allowOutsideClick) return;
228
+ this.onCancel();
132
229
  }
133
- /** @deprecated このメソッドは非推奨です。代わりに`hide`メソッドを使用してください。 */
134
- stop() {
135
- this.hide();
230
+ onEscapeKey() {
231
+ if (!__privateGet(this, _state).allowEscapeKey) return;
232
+ this.onCancel();
136
233
  }
137
234
  };
235
+ _state = new WeakMap();
236
+ _listeners = new WeakMap();
237
+ _resolver = new WeakMap();
238
+ _timerId = new WeakMap();
239
+ _DialogController_instances = new WeakSet();
240
+ emit_fn = function() {
241
+ const snapshot = { ...__privateGet(this, _state) };
242
+ for (const fn of __privateGet(this, _listeners)) fn(snapshot);
243
+ };
244
+ update_fn = function(patch) {
245
+ Object.assign(__privateGet(this, _state), patch);
246
+ __privateMethod(this, _DialogController_instances, emit_fn).call(this);
247
+ };
248
+ // ─── Internal ────────────────────────────────────────────
249
+ createPromise_fn = function(timer) {
250
+ return new Promise((resolve) => {
251
+ __privateSet(this, _resolver, resolve);
252
+ if (timer != null && timer > 0) {
253
+ __privateSet(this, _timerId, setTimeout(() => {
254
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { ...createInitialState(), open: false });
255
+ resolve({ isConfirmed: false, isCanceled: false, isDismissed: true });
256
+ __privateSet(this, _resolver, null);
257
+ }, timer));
258
+ }
259
+ });
260
+ };
261
+ resolve_fn = function(result) {
262
+ const resolver = __privateGet(this, _resolver);
263
+ __privateSet(this, _resolver, null);
264
+ resolver?.(result);
265
+ };
266
+ clearTimer_fn = function() {
267
+ if (__privateGet(this, _timerId) != null) {
268
+ clearTimeout(__privateGet(this, _timerId));
269
+ __privateSet(this, _timerId, null);
270
+ }
271
+ };
272
+ updateItemStatus_fn = function(key, label, status) {
273
+ const items = [...__privateGet(this, _state)[key]];
274
+ const idx = items.findIndex((i) => i.label === label);
275
+ if (idx >= 0) {
276
+ items[idx] = { ...items[idx], status };
277
+ __privateMethod(this, _DialogController_instances, update_fn).call(this, { [key]: items });
278
+ }
279
+ };
280
+
281
+ // src/overlay-dialog.ts
282
+ var import_lit2 = require("lit");
283
+ var import_decorators = require("lit/decorators.js");
284
+ var import_unsafe_html = require("lit/directives/unsafe-html.js");
138
285
 
139
- // src/loading-overlay/style.ts
140
- var import_css2 = require("@emotion/css");
141
- var containerStyle = import_css2.css`
142
- display: flex;
143
- flex-direction: column;
144
- align-items: center;
145
- justify-content: center;
146
- gap: 32px;
147
- padding: 24px;
148
- background-color: #fff;
149
- border-radius: 0;
150
- box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
151
- border: 1px solid #f3f4f6;
152
- min-height: 200px;
153
- position: relative;
154
- overflow: hidden;
155
- transition: all 250ms ease;
286
+ // src/styles.ts
287
+ var import_lit = require("lit");
288
+ var overlayStyles = import_lit.css`
289
+ :host {
290
+ /* ─── Customizable CSS Variables ─── */
291
+ --dialog-font-family:
292
+ 'Yu Gothic Medium', '游ゴシック', YuGothic, 'メイリオ', 'Hiragino Kaku Gothic ProN', Meiryo,
293
+ sans-serif;
294
+ --dialog-text-color: #356;
295
+ --dialog-font-size: 14px;
296
+ --dialog-font-size-desktop: 16px;
297
+ --dialog-z-index: 1000;
298
+ --dialog-backdrop-color: rgb(255 255 255 / 0.73);
299
+ --dialog-backdrop-blur: 4px;
300
+ --dialog-transition-duration: 250ms;
301
+
302
+ /* Card */
303
+ --dialog-card-bg: #fff;
304
+ --dialog-card-border: #f3f4f6;
305
+ --dialog-card-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
306
+ --dialog-card-radius: 4px;
307
+ --dialog-card-width: 400px;
308
+ --dialog-card-min-height: 200px;
309
+ --dialog-card-padding: 24px;
310
+ --dialog-card-padding-desktop: 32px;
311
+
312
+ /* Colors */
313
+ --dialog-primary: #3b82f6;
314
+ --dialog-primary-hover: #2563eb;
315
+ --dialog-success: #22c55e;
316
+ --dialog-error: #ef4444;
317
+ --dialog-warning: #f59e0b;
318
+ --dialog-info: #3b82f6;
319
+
320
+ /* Progress */
321
+ --dialog-progress-height: 2px;
322
+ --dialog-progress-color: var(--dialog-primary-hover);
323
+ --dialog-progress-transition: all 350ms ease;
324
+
325
+ /* Button */
326
+ --dialog-btn-radius: 6px;
327
+ --dialog-btn-padding: 8px 24px;
328
+ --dialog-btn-font-size: 14px;
329
+
330
+ /* Spinner */
331
+ --dialog-spinner-size: 60px;
332
+ --dialog-spinner-track: rgb(59 130 246 / 0.2);
333
+ --dialog-spinner-arc: var(--dialog-primary);
334
+
335
+ display: contents;
336
+ font-family: var(--dialog-font-family);
337
+ color: var(--dialog-text-color);
338
+ font-size: var(--dialog-font-size);
339
+ }
340
+
341
+ /* ─── Backdrop ─── */
342
+
343
+ .backdrop {
344
+ position: fixed;
345
+ inset: 0;
346
+ width: 100vw;
347
+ height: 100vh;
348
+ display: grid;
349
+ place-items: end stretch;
350
+ z-index: var(--dialog-z-index);
351
+ overflow: hidden;
352
+ background-color: var(--dialog-backdrop-color);
353
+ backdrop-filter: blur(var(--dialog-backdrop-blur));
354
+ box-sizing: border-box;
355
+ transition: opacity var(--dialog-transition-duration) ease;
356
+ opacity: 0;
357
+ pointer-events: none;
358
+ }
359
+
360
+ .backdrop[data-open] {
361
+ opacity: 1;
362
+ pointer-events: all;
363
+ }
156
364
 
157
365
  @media (min-width: 640px) {
158
- width: 400px;
159
- max-width: 90vw;
160
- border-radius: 4px;
161
- padding: 32px;
366
+ :host {
367
+ font-size: var(--dialog-font-size-desktop);
368
+ }
369
+ .backdrop {
370
+ place-items: center;
371
+ }
162
372
  }
163
- `;
164
- var getLoaderStyle = () => import_css2.css`
165
- font-size: 60px;
166
- width: 1em;
167
- height: 1em;
168
- border-radius: 50%;
169
- box-shadow: inset 0 0 0 1px #3b82f633;
170
- position: relative;
171
373
 
172
- animation: none;
173
- &[${ATTRIBUTE_ANIMATION}] {
174
- animation: rotate 1.2s infinite linear;
374
+ /* ─── Card ─── */
375
+
376
+ .card {
377
+ display: flex;
378
+ flex-direction: column;
379
+ align-items: center;
380
+ justify-content: center;
381
+ gap: 16px;
382
+ padding: var(--dialog-card-padding);
383
+ background-color: var(--dialog-card-bg);
384
+ border-radius: 0;
385
+ box-shadow: var(--dialog-card-shadow);
386
+ border: 1px solid var(--dialog-card-border);
387
+ min-height: var(--dialog-card-min-height);
388
+ position: relative;
389
+ overflow: hidden;
390
+ transition: all var(--dialog-transition-duration) ease;
391
+ }
392
+
393
+ @media (min-width: 640px) {
394
+ .card {
395
+ width: var(--dialog-card-width);
396
+ max-width: 90vw;
397
+ border-radius: var(--dialog-card-radius);
398
+ padding: var(--dialog-card-padding-desktop);
399
+ }
175
400
  }
176
401
 
177
- > div {
402
+ /* ─── Spinner ─── */
403
+
404
+ .spinner {
405
+ font-size: var(--dialog-spinner-size);
406
+ width: 1em;
407
+ height: 1em;
408
+ border-radius: 50%;
409
+ box-shadow: inset 0 0 0 1px var(--dialog-spinner-track);
410
+ position: relative;
411
+ animation: spin 1.2s infinite linear;
412
+ }
413
+
414
+ .spinner-half {
178
415
  position: absolute;
179
416
  left: 50%;
180
417
  top: 50%;
@@ -184,17 +421,18 @@ var getLoaderStyle = () => import_css2.css`
184
421
  margin-top: -0.5em;
185
422
  overflow: hidden;
186
423
  transform-origin: 0.5em 0.5em;
187
- mask-image: linear-gradient(top, #000f, #0000);
188
- -webkit-mask-image: -webkit-linear-gradient(top, #000f, #0000);
189
-
190
- > div {
191
- width: 1em;
192
- height: 1em;
193
- border-radius: 50%;
194
- box-shadow: inset 0 0 0 1px #3b82f6;
195
- }
424
+ mask-image: linear-gradient(to bottom, #000f, #0000);
425
+ -webkit-mask-image: linear-gradient(to bottom, #000f, #0000);
426
+ }
427
+
428
+ .spinner-inner {
429
+ width: 1em;
430
+ height: 1em;
431
+ border-radius: 50%;
432
+ box-shadow: inset 0 0 0 1px var(--dialog-spinner-arc);
196
433
  }
197
- @keyframes rotate {
434
+
435
+ @keyframes spin {
198
436
  0% {
199
437
  transform: rotate(0deg);
200
438
  }
@@ -202,315 +440,220 @@ var getLoaderStyle = () => import_css2.css`
202
440
  transform: rotate(360deg);
203
441
  }
204
442
  }
205
- `;
206
- var progressStyle = import_css2.css`
207
- position: absolute;
208
- bottom: 0px;
209
- left: 0;
210
- width: 0%;
211
- height: 2px;
212
- background-color: #2563eb;
213
- transition: all 350ms ease;
214
- `;
215
443
 
216
- // src/loading-overlay/index.ts
217
- var ATTRIBUTE_ANIMATION = "data-animation";
218
- var LoadingOverlay = class extends Overlay {
219
- #label;
220
- #html;
221
- #progress;
222
- _loaderElement;
223
- _progressElement;
224
- _contentElement;
225
- constructor(props = {}) {
226
- super();
227
- this.#label = props.label ?? "";
228
- this.#html = "";
229
- this.#progress = props.progress ?? null;
230
- const container = document.createElement("div");
231
- container.classList.add(containerStyle);
232
- this.root.append(container);
233
- const loaderElement = document.createElement("div");
234
- loaderElement.innerHTML = "<div><div></div></div>";
235
- loaderElement.classList.add(getLoaderStyle());
236
- this._loaderElement = loaderElement;
237
- container.append(loaderElement);
238
- const progressElement = document.createElement("div");
239
- progressElement.classList.add(progressStyle);
240
- this._progressElement = progressElement;
241
- container.append(progressElement);
242
- const contentElement = document.createElement("div");
243
- this._contentElement = contentElement;
244
- container.append(contentElement);
245
- this.render();
246
- }
247
- show() {
248
- super.show();
249
- this._loaderElement.setAttribute(ATTRIBUTE_ANIMATION, "");
444
+ /* ─── Icon ─── */
445
+
446
+ .icon-container {
447
+ width: 64px;
448
+ height: 64px;
449
+ border-radius: 50%;
450
+ display: flex;
451
+ align-items: center;
452
+ justify-content: center;
453
+ flex-shrink: 0;
250
454
  }
251
- hide() {
252
- super.hide();
253
- this._loaderElement.removeAttribute(ATTRIBUTE_ANIMATION);
254
- this.#progress = 0;
255
- this.#html = "";
256
- this.#label = "";
257
- }
258
- set label(label) {
259
- this.#label = label;
260
- this.render();
261
- }
262
- set html(html) {
263
- this.#html = html;
264
- this.render();
265
- }
266
- /**
267
- * 進捗状況を設定します。
268
- * @param percent 進捗のパーセンテージ(0-100)
269
- */
270
- set progress(percent) {
271
- this.#progress = percent;
272
- this.render();
455
+
456
+ .icon-container svg {
457
+ width: 36px;
458
+ height: 36px;
273
459
  }
274
- render() {
275
- super.render();
276
- this._progressElement.style.width = `${this.#progress}%`;
277
- if (this.#html) {
278
- this._contentElement.innerHTML = this.#html;
279
- } else {
280
- if (this.#label instanceof Array) {
281
- this._contentElement.innerHTML = `<div>${this.#label.join("</div><div>")}</div>`;
282
- } else {
283
- this._contentElement.innerText = this.#label;
284
- }
285
- }
460
+
461
+ .icon-success {
462
+ background-color: rgb(34 197 94 / 0.1);
463
+ color: var(--dialog-success);
286
464
  }
287
- };
288
465
 
289
- // src/task-list-overlay.ts
290
- var import_css3 = require("@emotion/css");
291
- var TaskListOverlay = class extends Overlay {
292
- #taskList;
293
- _contentElement;
294
- constructor(props = {}) {
295
- super();
296
- this.#taskList = props.taskList ?? [];
297
- const container = document.createElement("div");
298
- container.classList.add(this.containerStyle);
299
- this.root.append(container);
300
- const contentElement = document.createElement("div");
301
- this._contentElement = contentElement;
302
- contentElement.classList.add(this.contentStyle);
303
- container.append(contentElement);
304
- this.render();
466
+ .icon-error {
467
+ background-color: rgb(239 68 68 / 0.1);
468
+ color: var(--dialog-error);
305
469
  }
306
- hide() {
307
- this.#taskList = [];
308
- super.hide();
309
- }
310
- done(key) {
311
- const task = this.#taskList.find((t) => t.key === key);
312
- if (task) {
313
- task.status = "done";
314
- this.render();
315
- }
470
+
471
+ .icon-warning {
472
+ background-color: rgb(245 158 11 / 0.1);
473
+ color: var(--dialog-warning);
316
474
  }
317
- error(key) {
318
- const task = this.#taskList.find((t) => t.key === key);
319
- if (task) {
320
- task.status = "error";
321
- this.render();
475
+
476
+ .icon-info {
477
+ background-color: rgb(59 130 246 / 0.1);
478
+ color: var(--dialog-info);
479
+ }
480
+
481
+ /* ─── Check Animation ─── */
482
+
483
+ .check-circle {
484
+ width: 64px;
485
+ height: 64px;
486
+ border-radius: 50%;
487
+ stroke-width: 2;
488
+ stroke: var(--dialog-success);
489
+ fill: none;
490
+ stroke-dasharray: 200;
491
+ stroke-dashoffset: 200;
492
+ animation: check-circle-draw 0.6s ease forwards;
493
+ }
494
+
495
+ .check-mark {
496
+ stroke: var(--dialog-success);
497
+ stroke-width: 3;
498
+ stroke-linecap: round;
499
+ stroke-linejoin: round;
500
+ fill: none;
501
+ stroke-dasharray: 50;
502
+ stroke-dashoffset: 50;
503
+ animation: check-mark-draw 0.4s 0.4s ease forwards;
504
+ }
505
+
506
+ @keyframes check-circle-draw {
507
+ to {
508
+ stroke-dashoffset: 0;
322
509
  }
323
510
  }
324
- inProgress(key) {
325
- const task = this.#taskList.find((t) => t.key === key);
326
- if (task) {
327
- task.status = "in-progress";
328
- this.render();
511
+
512
+ @keyframes check-mark-draw {
513
+ to {
514
+ stroke-dashoffset: 0;
329
515
  }
330
516
  }
331
- addTask(...tasks) {
332
- this.#taskList.push(...tasks.map(({ key, label }) => ({ key, label, status: "new" })));
333
- this.render();
517
+
518
+ /* ─── Text ─── */
519
+
520
+ .label {
521
+ font-size: 18px;
522
+ font-weight: 600;
523
+ color: #1f2937;
524
+ text-align: center;
525
+ margin: 0;
526
+ word-break: break-word;
334
527
  }
335
- render() {
336
- super.render();
337
- this._contentElement.innerHTML = `
338
- <div class="${this.taskListContainerStyle}">
339
- ${this.#taskList.map((task) => this.renderTask(task)).join("")}
340
- </div>
341
- `;
528
+
529
+ .description {
530
+ font-size: 14px;
531
+ color: #6b7280;
532
+ text-align: center;
533
+ margin: 0;
534
+ word-break: break-word;
535
+ line-height: 1.6;
342
536
  }
343
- renderTask(task) {
344
- const label = Array.isArray(task.label) ? task.label.join(" ") : task.label;
345
- const status = task.status;
346
- return `
347
- <div class="${this.taskContainerStyle}">
348
- <div class="${this.taskStatusStyle} status-${status}">${this.getTaskStatusElement(
349
- task.status
350
- )}</div>
351
- <div class="${this.taskLabelStyle}">${label}</div>
352
- </div>
353
- `;
537
+
538
+ .html-content {
539
+ font-size: 14px;
540
+ color: #6b7280;
541
+ text-align: center;
542
+ word-break: break-word;
543
+ line-height: 1.6;
544
+ width: 100%;
354
545
  }
355
- getTaskStatusElement(status) {
356
- switch (status) {
357
- case "new":
358
- return `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="#ddd" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-minus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="8" y1="12" x2="16" y2="12"></line></svg>`;
359
- case "in-progress":
360
- return `<div><div></div></div>`;
361
- case "done":
362
- return `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="#80beaf" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>`;
363
- default:
364
- return ``;
365
- }
546
+
547
+ /* ─── Progress ─── */
548
+
549
+ .progress-bar {
550
+ position: absolute;
551
+ bottom: 0;
552
+ left: 0;
553
+ width: 0%;
554
+ height: var(--dialog-progress-height);
555
+ background-color: var(--dialog-progress-color);
556
+ transition: var(--dialog-progress-transition);
366
557
  }
367
- containerStyle = import_css3.css`
558
+
559
+ /* ─── Buttons ─── */
560
+
561
+ .actions {
368
562
  display: flex;
369
563
  flex-direction: column;
370
- align-items: center;
371
- justify-content: center;
372
- gap: 32px;
373
- padding: 32px 64px;
374
- background-color: #fffc;
375
- border-radius: 8px;
376
- box-shadow: 0 5px 24px -6px #0002;
377
- width: 300px;
378
- max-width: 90vw;
379
- min-height: 200px;
380
- position: relative;
381
- overflow: hidden;
382
- transition: all 250ms ease;
564
+ gap: 8px;
565
+ width: 100%;
566
+ margin-top: 8px;
567
+ }
383
568
 
384
- @keyframes spin {
385
- 0% {
386
- transform: rotate(0deg);
387
- border-radius: 1em;
388
- }
389
- 20% {
390
- transform: rotate(0deg);
391
- }
392
- 30%,
393
- 60% {
394
- border-radius: 0.25em;
395
- }
396
- 70% {
397
- transform: rotate(180deg);
398
- }
399
- 100% {
400
- transform: rotate(180deg);
401
- border-radius: 1em;
402
- }
569
+ @media (min-width: 640px) {
570
+ .actions {
571
+ flex-direction: row;
572
+ justify-content: center;
403
573
  }
404
- `;
405
- contentStyle = import_css3.css`
406
- width: 100%;
407
- `;
408
- taskListContainerStyle = import_css3.css`
574
+ }
575
+
576
+ .btn {
577
+ padding: var(--dialog-btn-padding);
578
+ font-size: var(--dialog-btn-font-size);
579
+ font-family: inherit;
580
+ border: none;
581
+ border-radius: var(--dialog-btn-radius);
582
+ cursor: pointer;
583
+ font-weight: 500;
584
+ transition:
585
+ background-color 150ms ease,
586
+ transform 80ms ease;
587
+ min-width: 100px;
588
+ text-align: center;
589
+ }
590
+
591
+ .btn:active {
592
+ transform: scale(0.97);
593
+ }
594
+
595
+ .btn-confirm {
596
+ background-color: var(--dialog-primary);
597
+ color: #fff;
598
+ }
599
+
600
+ .btn-confirm:hover {
601
+ background-color: var(--dialog-primary-hover);
602
+ }
603
+
604
+ .btn-cancel {
605
+ background-color: #f3f4f6;
606
+ color: #374151;
607
+ }
608
+
609
+ .btn-cancel:hover {
610
+ background-color: #e5e7eb;
611
+ }
612
+
613
+ /* ─── Queue / Steps list ─── */
614
+
615
+ .task-list {
409
616
  display: flex;
410
617
  flex-direction: column;
411
- gap: 1em;
618
+ gap: 12px;
412
619
  width: 100%;
413
- `;
414
- taskContainerStyle = import_css3.css`
620
+ padding: 0;
621
+ margin: 0;
622
+ list-style: none;
623
+ }
624
+
625
+ .task-item {
415
626
  display: flex;
416
627
  align-items: center;
417
- gap: 1em;
418
- `;
419
- taskLabelStyle = import_css3.css``;
420
- taskStatusStyle = import_css3.css`
421
- font-size: 32px;
422
- width: 1em;
423
- height: 1em;
628
+ gap: 12px;
629
+ font-size: 14px;
630
+ }
631
+
632
+ .task-icon {
633
+ width: 24px;
634
+ height: 24px;
424
635
  display: flex;
425
- justify-content: center;
426
636
  align-items: center;
427
-
428
- &.status-new {
429
- }
430
-
431
- &.status-in-progress {
432
- border-radius: 50%;
433
- box-shadow: inset 0 0 0 1px #3b82f633;
434
- position: relative;
435
- animation: rotate 1.2s infinite linear;
436
- > div {
437
- position: absolute;
438
- left: 50%;
439
- top: 50%;
440
- width: 0.5em;
441
- height: 1em;
442
- margin-left: -0.5em;
443
- margin-top: -0.5em;
444
- overflow: hidden;
445
- transform-origin: 0.5em 0.5em;
446
- mask-image: linear-gradient(top, #000f, #0000);
447
- -webkit-mask-image: -webkit-linear-gradient(top, #000f, #0000);
448
-
449
- > div {
450
- width: 1em;
451
- height: 1em;
452
- border-radius: 50%;
453
- box-shadow: inset 0 0 0 1px #3b82f6;
454
- }
455
- }
456
- @keyframes rotate {
457
- 0% {
458
- transform: rotate(0deg);
459
- }
460
- 100% {
461
- transform: rotate(360deg);
462
- }
463
- }
464
- }
465
- `;
466
- };
467
-
468
- // src/modal/style.ts
469
- var import_css4 = require("@emotion/css");
470
- var containerStyle2 = import_css4.css`
471
- display: grid;
472
- gap: 2px;
473
- padding: 24px;
474
- background-color: #fff;
475
- border-radius: 0;
476
- box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
477
- border: 1px solid #f3f4f6;
478
- min-height: 200px;
479
- position: relative;
480
- overflow: hidden;
481
- transition: all 250ms ease;
482
-
483
- @media (min-width: 640px) {
484
- width: 400px;
485
- max-width: 90vw;
486
- border-radius: 4px;
487
- padding: 32px;
637
+ justify-content: center;
638
+ flex-shrink: 0;
488
639
  }
489
- `;
490
- var iconStyle = import_css4.css``;
491
- var titleStyle = import_css4.css`
492
- font-size: 18px;
493
- font-weight: 600;
494
- color: #1f2937;
495
- text-align: center;
496
- @media (min-width: 640px) {
497
- text-align: left;
640
+
641
+ .task-icon svg {
642
+ width: 20px;
643
+ height: 20px;
498
644
  }
499
- `;
500
- var loaderStyle = import_css4.css`
501
- font-size: 60px;
502
- width: 1em;
503
- height: 1em;
504
- border-radius: 50%;
505
- box-shadow: inset 0 0 0 1px #3b82f633;
506
- position: relative;
507
645
 
508
- animation: none;
509
- &[data-state='loading'] {
510
- animation: rotate 1s linear infinite;
646
+ .task-icon .mini-spinner {
647
+ font-size: 20px;
648
+ width: 1em;
649
+ height: 1em;
650
+ border-radius: 50%;
651
+ box-shadow: inset 0 0 0 1px var(--dialog-spinner-track);
652
+ position: relative;
653
+ animation: spin 1.2s infinite linear;
511
654
  }
512
655
 
513
- > div {
656
+ .task-icon .mini-spinner-half {
514
657
  position: absolute;
515
658
  left: 50%;
516
659
  top: 50%;
@@ -520,244 +663,522 @@ var loaderStyle = import_css4.css`
520
663
  margin-top: -0.5em;
521
664
  overflow: hidden;
522
665
  transform-origin: 0.5em 0.5em;
523
- mask-image: linear-gradient(top, #000f, #0000);
524
- -webkit-mask-image: -webkit-linear-gradient(top, #000f, #0000);
525
-
526
- > div {
527
- width: 1em;
528
- height: 1em;
529
- border-radius: 50%;
530
- box-shadow: inset 0 0 0 1px #3b82f6;
531
- }
666
+ mask-image: linear-gradient(to bottom, #000f, #0000);
667
+ -webkit-mask-image: linear-gradient(to bottom, #000f, #0000);
532
668
  }
533
- @keyframes rotate {
534
- 0% {
535
- transform: rotate(0deg);
536
- }
537
- 100% {
538
- transform: rotate(360deg);
539
- }
669
+
670
+ .task-icon .mini-spinner-inner {
671
+ width: 1em;
672
+ height: 1em;
673
+ border-radius: 50%;
674
+ box-shadow: inset 0 0 0 1px var(--dialog-spinner-arc);
540
675
  }
541
- `;
542
- var progressStyle2 = import_css4.css`
543
- position: absolute;
544
- bottom: 0px;
545
- left: 0;
546
- width: 0%;
547
- height: 2px;
548
- background-color: #2563eb;
549
- transition: all 350ms ease;
550
- `;
551
- var actionsStyle = import_css4.css`
552
- display: flex;
553
- flex-direction: column;
554
- justify-content: flex-end;
555
- align-items: flex-end;
556
- gap: 8px;
557
676
 
558
- @media (min-width: 640px) {
559
- flex-direction: row;
677
+ .task-label {
678
+ color: #6b7280;
679
+ }
680
+
681
+ .task-label[data-status='active'] {
682
+ color: var(--dialog-text-color);
683
+ font-weight: 500;
684
+ }
685
+
686
+ .task-label[data-status='done'] {
687
+ color: var(--dialog-success);
688
+ }
689
+
690
+ .task-label[data-status='error'] {
691
+ color: var(--dialog-error);
692
+ }
693
+
694
+ .task-label[data-status='skipped'] {
695
+ color: #9ca3af;
696
+ text-decoration: line-through;
697
+ }
698
+
699
+ /* ─── Queue ellipsis ─── */
700
+
701
+ .queue-ellipsis {
702
+ display: flex;
703
+ flex-direction: column;
704
+ align-items: center;
705
+ gap: 3px;
706
+ padding: 4px 0;
707
+ /* アイコン列 (24px) の中央に配置: (24px - 4px dot) / 2 = 10px */
708
+ padding-left: 10px;
709
+ align-self: flex-start;
710
+ list-style: none;
711
+ }
712
+
713
+ .queue-ellipsis span {
714
+ display: block;
715
+ width: 4px;
716
+ height: 4px;
717
+ border-radius: 50%;
718
+ background-color: #d1d5db;
719
+ }
720
+
721
+ /* ─── Steps indicator ─── */
722
+
723
+ .steps-header {
724
+ display: flex;
725
+ align-items: center;
726
+ gap: 4px;
727
+ width: 100%;
728
+ justify-content: center;
729
+ margin-bottom: 8px;
730
+ }
731
+
732
+ .step-dot {
733
+ width: 8px;
734
+ height: 8px;
735
+ border-radius: 50%;
736
+ background-color: #d1d5db;
737
+ transition: background-color 200ms ease;
738
+ }
739
+
740
+ .step-dot[data-status='active'] {
741
+ background-color: var(--dialog-primary);
742
+ box-shadow: 0 0 0 3px rgb(59 130 246 / 0.2);
743
+ }
744
+
745
+ .step-dot[data-status='done'] {
746
+ background-color: var(--dialog-success);
747
+ }
748
+
749
+ .step-dot[data-status='error'] {
750
+ background-color: var(--dialog-error);
751
+ }
752
+
753
+ .step-dot[data-status='skipped'] {
754
+ background-color: #9ca3af;
755
+ }
756
+
757
+ .step-connector {
758
+ flex: 1;
759
+ max-width: 24px;
760
+ height: 2px;
761
+ background-color: #e5e7eb;
560
762
  }
561
763
  `;
562
764
 
563
- // src/modal/index.ts
564
- var Modal = class extends Overlay {
565
- #title;
566
- #html;
567
- #label;
568
- #progress;
569
- #state;
570
- _containerElement;
571
- _iconElement;
572
- _titleElement;
573
- _loaderElement;
574
- _progressElement;
575
- _contentElement;
576
- _actionsElement;
577
- _okButtonElement;
578
- _cancelButtonElement;
579
- constructor(props = {}) {
580
- super();
581
- this.#title = "";
582
- this.#html = "";
583
- this.#label = props.label ?? "";
584
- this.#progress = props.progress ?? null;
585
- this.#state = "hidden";
586
- const container = document.createElement("div");
587
- container.classList.add(containerStyle2);
588
- this._containerElement = container;
589
- this.root.append(container);
590
- const iconElement = document.createElement("div");
591
- iconElement.classList.add(iconStyle);
592
- this._iconElement = iconElement;
593
- container.append(iconElement);
594
- const titleElement = document.createElement("div");
595
- titleElement.classList.add(titleStyle);
596
- this._titleElement = titleElement;
597
- container.append(titleElement);
598
- const loaderElement = document.createElement("div");
599
- loaderElement.innerHTML = "<div><div></div></div>";
600
- loaderElement.classList.add(loaderStyle);
601
- this._loaderElement = loaderElement;
602
- const progressElement = document.createElement("div");
603
- progressElement.classList.add(progressStyle2);
604
- this._progressElement = progressElement;
605
- container.append(progressElement);
606
- const contentElement = document.createElement("div");
607
- this._contentElement = contentElement;
608
- container.append(contentElement);
609
- const actionsElement = document.createElement("div");
610
- actionsElement.classList.add(actionsStyle);
611
- this._actionsElement = actionsElement;
612
- container.append(actionsElement);
613
- const okButtonElement = document.createElement("button");
614
- okButtonElement.type = "button";
615
- okButtonElement.textContent = "OK";
616
- this._okButtonElement = okButtonElement;
617
- actionsElement.append(okButtonElement);
618
- const cancelButtonElement = document.createElement("button");
619
- cancelButtonElement.type = "button";
620
- cancelButtonElement.textContent = "\u30AD\u30E3\u30F3\u30BB\u30EB";
621
- this._cancelButtonElement = cancelButtonElement;
622
- actionsElement.append(cancelButtonElement);
623
- this.render();
624
- }
625
- alert(params) {
626
- const {
627
- title = "",
628
- text = "",
629
- icon = "",
630
- disableClose = false,
631
- disableEscape = false
632
- } = params;
633
- this.#title = title;
634
- this.#label = text;
635
- this.changeState("alert");
636
- this.show();
637
- return new Promise((resolve) => {
638
- this._okButtonElement.addEventListener("click", () => {
639
- if (!disableClose) {
640
- this.hide();
641
- }
642
- resolve({
643
- isConfirmed: true
644
- });
645
- });
646
- this._cancelButtonElement.addEventListener("click", () => {
647
- if (!disableClose) {
648
- this.hide();
649
- }
650
- resolve({
651
- isConfirmed: false
652
- });
653
- });
654
- this.root.addEventListener("click", (event) => {
655
- if (event.currentTarget === event.target) {
656
- if (!disableClose) {
657
- this.hide();
658
- }
659
- resolve({
660
- isConfirmed: false
661
- });
662
- }
663
- });
664
- this.root.addEventListener("keydown", (event) => {
665
- if (event.key === "Escape") {
666
- if (!disableEscape) {
667
- this.hide();
668
- }
669
- resolve({
670
- isConfirmed: false
671
- });
765
+ // src/overlay-dialog.ts
766
+ var OverlayDialog = class extends import_lit2.LitElement {
767
+ constructor() {
768
+ super(...arguments);
769
+ this._state = createInitialState();
770
+ this._beforeUnloadHandler = (e) => e.preventDefault();
771
+ this._onKeyDown = (e) => {
772
+ if (e.key === "Escape" && this._state.open) {
773
+ this.controller.onEscapeKey();
774
+ }
775
+ };
776
+ }
777
+ connectedCallback() {
778
+ super.connectedCallback();
779
+ if (this.controller) {
780
+ this._state = { ...this.controller.state };
781
+ this._unsubscribe = this.controller.subscribe((s) => {
782
+ const wasOpen = this._state.open;
783
+ this._state = s;
784
+ this._syncBodyScroll(s.open);
785
+ if (s.open && !wasOpen) {
786
+ window.addEventListener("beforeunload", this._beforeUnloadHandler);
787
+ } else if (!s.open && wasOpen) {
788
+ window.removeEventListener("beforeunload", this._beforeUnloadHandler);
672
789
  }
673
790
  });
674
- });
791
+ }
792
+ window.addEventListener("keydown", this._onKeyDown);
675
793
  }
676
- loading() {
677
- this._iconElement.append(this._loaderElement);
678
- this.changeState("loading");
679
- this.show();
794
+ disconnectedCallback() {
795
+ super.disconnectedCallback();
796
+ this._unsubscribe?.();
797
+ this._syncBodyScroll(false);
798
+ window.removeEventListener("beforeunload", this._beforeUnloadHandler);
799
+ window.removeEventListener("keydown", this._onKeyDown);
680
800
  }
681
- hide() {
682
- this.#progress = 0;
683
- this.#html = "";
684
- this.#label = "";
685
- this.changeState("hidden");
686
- super.hide();
687
- }
688
- set label(label) {
689
- this.#label = label;
690
- this.render();
691
- }
692
- set html(html) {
693
- this.#html = html;
694
- this.render();
695
- }
696
- set progress(progress) {
697
- this.#progress = progress;
698
- this.render();
699
- }
700
- changeState(state) {
701
- this.#state = state;
702
- const elements = [
703
- this.root,
704
- this._actionsElement,
705
- this._contentElement,
706
- this._iconElement,
707
- this._loaderElement,
708
- this._progressElement,
709
- this._titleElement
710
- ];
711
- for (const element of elements) {
712
- element.dataset.state = state;
801
+ _syncBodyScroll(lock) {
802
+ if (lock) {
803
+ document.body.style.overflow = "hidden";
804
+ } else {
805
+ document.body.style.overflow = "";
713
806
  }
714
- this.render();
715
807
  }
716
- render() {
717
- super.render();
718
- this._progressElement.style.width = `${this.#progress}%`;
719
- switch (this.#state) {
720
- case "loading":
721
- break;
722
- case "alert":
723
- this._iconElement.innerHTML = "";
724
- break;
725
- case "hidden":
808
+ // ─── Event Handlers ──────────────────────────────────────
809
+ _onBackdropClick(e) {
810
+ if (e.target === e.currentTarget) {
811
+ this.controller.onOutsideClick();
812
+ }
813
+ }
814
+ // ─── Render Helpers ──────────────────────────────────────
815
+ _renderIcon(icon) {
816
+ if (!icon) return import_lit2.nothing;
817
+ if (icon === "success") {
818
+ return import_lit2.html`
819
+ <svg
820
+ class="icon-container"
821
+ viewBox="0 0 64 64"
822
+ style="width:64px;height:64px;background:none;"
823
+ >
824
+ <circle class="check-circle" cx="32" cy="32" r="30" />
825
+ <polyline class="check-mark" points="20,34 28,42 44,24" />
826
+ </svg>
827
+ `;
828
+ }
829
+ const paths = {
830
+ error: import_lit2.html`<svg
831
+ viewBox="0 0 24 24"
832
+ fill="none"
833
+ stroke="currentColor"
834
+ stroke-width="2"
835
+ stroke-linecap="round"
836
+ stroke-linejoin="round"
837
+ >
838
+ <circle cx="12" cy="12" r="10" />
839
+ <line x1="15" y1="9" x2="9" y2="15" />
840
+ <line x1="9" y1="9" x2="15" y2="15" />
841
+ </svg>`,
842
+ warning: import_lit2.html`<svg
843
+ viewBox="0 0 24 24"
844
+ fill="none"
845
+ stroke="currentColor"
846
+ stroke-width="2"
847
+ stroke-linecap="round"
848
+ stroke-linejoin="round"
849
+ >
850
+ <path
851
+ 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"
852
+ />
853
+ <line x1="12" y1="9" x2="12" y2="13" />
854
+ <line x1="12" y1="17" x2="12.01" y2="17" />
855
+ </svg>`,
856
+ info: import_lit2.html`<svg
857
+ viewBox="0 0 24 24"
858
+ fill="none"
859
+ stroke="currentColor"
860
+ stroke-width="2"
861
+ stroke-linecap="round"
862
+ stroke-linejoin="round"
863
+ >
864
+ <circle cx="12" cy="12" r="10" />
865
+ <line x1="12" y1="16" x2="12" y2="12" />
866
+ <line x1="12" y1="8" x2="12.01" y2="8" />
867
+ </svg>`
868
+ };
869
+ return import_lit2.html` <div class="icon-container icon-${icon}">${paths[icon] ?? import_lit2.nothing}</div> `;
870
+ }
871
+ _renderSpinner() {
872
+ return import_lit2.html`
873
+ <div class="spinner">
874
+ <div class="spinner-half">
875
+ <div class="spinner-inner"></div>
876
+ </div>
877
+ </div>
878
+ `;
879
+ }
880
+ _renderTaskIcon(status) {
881
+ switch (status) {
882
+ case "active":
883
+ return import_lit2.html`
884
+ <div class="mini-spinner">
885
+ <div class="mini-spinner-half">
886
+ <div class="mini-spinner-inner"></div>
887
+ </div>
888
+ </div>
889
+ `;
890
+ case "done":
891
+ return import_lit2.html`<svg
892
+ viewBox="0 0 24 24"
893
+ fill="none"
894
+ stroke="#22c55e"
895
+ stroke-width="2"
896
+ stroke-linecap="round"
897
+ stroke-linejoin="round"
898
+ >
899
+ <circle cx="12" cy="12" r="10" />
900
+ <polyline points="9,12 11,14 15,10" />
901
+ </svg>`;
902
+ case "error":
903
+ return import_lit2.html`<svg
904
+ viewBox="0 0 24 24"
905
+ fill="none"
906
+ stroke="#ef4444"
907
+ stroke-width="2"
908
+ stroke-linecap="round"
909
+ stroke-linejoin="round"
910
+ >
911
+ <circle cx="12" cy="12" r="10" />
912
+ <line x1="15" y1="9" x2="9" y2="15" />
913
+ <line x1="9" y1="9" x2="15" y2="15" />
914
+ </svg>`;
915
+ case "skipped":
916
+ return import_lit2.html`<svg
917
+ viewBox="0 0 24 24"
918
+ fill="none"
919
+ stroke="#9ca3af"
920
+ stroke-width="2"
921
+ stroke-linecap="round"
922
+ stroke-linejoin="round"
923
+ >
924
+ <circle cx="12" cy="12" r="10" />
925
+ <line x1="8" y1="12" x2="16" y2="12" />
926
+ </svg>`;
927
+ case "pending":
726
928
  default:
727
- this._iconElement.innerHTML = "";
728
- break;
929
+ return import_lit2.html`<svg
930
+ viewBox="0 0 24 24"
931
+ fill="none"
932
+ stroke="#d1d5db"
933
+ stroke-width="2"
934
+ stroke-linecap="round"
935
+ stroke-linejoin="round"
936
+ >
937
+ <circle cx="12" cy="12" r="10" />
938
+ </svg>`;
729
939
  }
730
- this._titleElement.textContent = this.#title;
731
- if (this.#html) {
732
- this._contentElement.innerHTML = this.#html;
733
- } else {
734
- if (this.#label instanceof Array) {
735
- this._contentElement.innerHTML = `<div>${this.#label.join("</div><div>")}</div>`;
736
- } else {
737
- this._contentElement.textContent = this.#label;
940
+ }
941
+ _getQueueWindow(items) {
942
+ const WINDOW = 3;
943
+ const total = items.length;
944
+ if (total <= WINDOW) return { start: 0, end: total - 1 };
945
+ let centerIdx = items.findIndex((i) => i.status === "active");
946
+ if (centerIdx < 0) {
947
+ const finishedStatuses = /* @__PURE__ */ new Set(["done", "skipped", "error"]);
948
+ const lastFinishedIdx = items.reduce(
949
+ (acc, item, i) => finishedStatuses.has(item.status) ? i : acc,
950
+ -1
951
+ );
952
+ centerIdx = lastFinishedIdx >= 0 ? Math.min(lastFinishedIdx + 1, total - 1) : 0;
953
+ }
954
+ const start = Math.max(0, Math.min(centerIdx - 1, total - WINDOW));
955
+ const end = Math.min(total - 1, start + WINDOW - 1);
956
+ return { start, end };
957
+ }
958
+ _renderQueueList(items) {
959
+ const total = items.length;
960
+ const { start, end } = this._getQueueWindow(items);
961
+ const visible = items.slice(start, end + 1);
962
+ return import_lit2.html`
963
+ <ul class="task-list">
964
+ ${start > 0 ? import_lit2.html`<li class="queue-ellipsis" aria-hidden="true">
965
+ <span></span><span></span><span></span>
966
+ </li>` : import_lit2.nothing}
967
+ ${visible.map(
968
+ (item) => import_lit2.html`
969
+ <li class="task-item">
970
+ <span class="task-icon">${this._renderTaskIcon(item.status)}</span>
971
+ <span class="task-label" data-status=${item.status}>${item.label}</span>
972
+ </li>
973
+ `
974
+ )}
975
+ ${end < total - 1 ? import_lit2.html`<li class="queue-ellipsis" aria-hidden="true">
976
+ <span></span><span></span><span></span>
977
+ </li>` : import_lit2.nothing}
978
+ </ul>
979
+ `;
980
+ }
981
+ _renderStepsHeader(items) {
982
+ const fragments = [];
983
+ items.forEach((item, i) => {
984
+ if (i > 0) {
985
+ fragments.push(import_lit2.html`<div class="step-connector"></div>`);
738
986
  }
987
+ fragments.push(import_lit2.html`<div class="step-dot" data-status=${item.status}></div>`);
988
+ });
989
+ return import_lit2.html`<div class="steps-header">${fragments}</div>`;
990
+ }
991
+ _renderStepsList(items) {
992
+ return import_lit2.html`
993
+ ${this._renderStepsHeader(items)}
994
+ <ul class="task-list">
995
+ ${items.map(
996
+ (item) => import_lit2.html`
997
+ <li class="task-item">
998
+ <span class="task-icon">${this._renderTaskIcon(item.status)}</span>
999
+ <span class="task-label" data-status=${item.status}>${item.label}</span>
1000
+ </li>
1001
+ `
1002
+ )}
1003
+ </ul>
1004
+ `;
1005
+ }
1006
+ _renderButtons() {
1007
+ const s = this._state;
1008
+ if (!s.showConfirmButton && !s.showCancelButton) return import_lit2.nothing;
1009
+ return import_lit2.html`
1010
+ <div class="actions">
1011
+ ${s.showConfirmButton ? import_lit2.html`<button class="btn btn-confirm" @click=${() => this.controller.onConfirm()}>
1012
+ ${s.confirmButtonText}
1013
+ </button>` : import_lit2.nothing}
1014
+ ${s.showCancelButton ? import_lit2.html`<button class="btn btn-cancel" @click=${() => this.controller.onCancel()}>
1015
+ ${s.cancelButtonText}
1016
+ </button>` : import_lit2.nothing}
1017
+ </div>
1018
+ `;
1019
+ }
1020
+ _renderBody() {
1021
+ const s = this._state;
1022
+ switch (s.dialogType) {
1023
+ case "loading":
1024
+ return import_lit2.html`
1025
+ ${this._renderSpinner()} ${s.label ? import_lit2.html`<p class="label">${s.label}</p>` : import_lit2.nothing}
1026
+ ${s.description ? import_lit2.html`<p class="description">${s.description}</p>` : import_lit2.nothing}
1027
+ `;
1028
+ case "alert":
1029
+ case "confirm":
1030
+ return import_lit2.html`
1031
+ ${this._renderIcon(s.icon)} ${s.label ? import_lit2.html`<p class="label">${s.label}</p>` : import_lit2.nothing}
1032
+ ${s.description ? import_lit2.html`<p class="description">${s.description}</p>` : import_lit2.nothing}
1033
+ ${s.html ? import_lit2.html`<div class="html-content">${(0, import_unsafe_html.unsafeHTML)(s.html)}</div>` : import_lit2.nothing}
1034
+ ${this._renderButtons()}
1035
+ `;
1036
+ case "queue":
1037
+ return import_lit2.html`
1038
+ ${s.label ? import_lit2.html`<p class="label">${s.label}</p>` : import_lit2.nothing}
1039
+ ${this._renderQueueList(s.queues)}
1040
+ `;
1041
+ case "steps":
1042
+ return import_lit2.html`
1043
+ ${s.label ? import_lit2.html`<p class="label">${s.label}</p>` : import_lit2.nothing}
1044
+ ${this._renderStepsList(s.steps)}
1045
+ `;
1046
+ default:
1047
+ return import_lit2.html`${import_lit2.nothing}`;
739
1048
  }
740
1049
  }
1050
+ render() {
1051
+ const s = this._state;
1052
+ return import_lit2.html`
1053
+ <div class="backdrop" ?data-open=${s.open} @click=${this._onBackdropClick}>
1054
+ <div class="card">
1055
+ ${this._renderBody()}
1056
+ <div
1057
+ class="progress-bar"
1058
+ style="width:${s.progress ?? 0}%;opacity:${s.progress !== null ? 1 : 0}"
1059
+ ></div>
1060
+ </div>
1061
+ </div>
1062
+ `;
1063
+ }
741
1064
  };
1065
+ OverlayDialog.styles = overlayStyles;
1066
+ __decorateClass([
1067
+ (0, import_decorators.property)({ attribute: false })
1068
+ ], OverlayDialog.prototype, "controller", 2);
1069
+ __decorateClass([
1070
+ (0, import_decorators.state)()
1071
+ ], OverlayDialog.prototype, "_state", 2);
1072
+ OverlayDialog = __decorateClass([
1073
+ (0, import_decorators.customElement)("overlay-dialog")
1074
+ ], OverlayDialog);
742
1075
 
743
- // src/utilities.ts
744
- var withLoading = (fn, label = "Loading...") => {
745
- return async (...args) => {
746
- const overlay = new LoadingOverlay({ label });
747
- overlay.show();
748
- try {
749
- return await fn(...args);
750
- } finally {
751
- overlay.hide();
752
- }
753
- };
1076
+ // src/dialog.ts
1077
+ var _controller, _element, _DialogSingleton_instances, ensureElement_fn;
1078
+ var DialogSingleton = class {
1079
+ constructor() {
1080
+ __privateAdd(this, _DialogSingleton_instances);
1081
+ __privateAdd(this, _controller, new DialogController());
1082
+ __privateAdd(this, _element, null);
1083
+ }
1084
+ // ─── Core ────────────────────────────────────────────────
1085
+ show(options) {
1086
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1087
+ __privateGet(this, _controller).show(options);
1088
+ }
1089
+ hide() {
1090
+ __privateGet(this, _controller).hide();
1091
+ }
1092
+ // ─── Alert / Confirm ─────────────────────────────────────
1093
+ alert(optionsOrLabel) {
1094
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1095
+ return __privateGet(this, _controller).alert(optionsOrLabel);
1096
+ }
1097
+ confirm(optionsOrLabel) {
1098
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1099
+ return __privateGet(this, _controller).confirm(optionsOrLabel);
1100
+ }
1101
+ // ─── Loading helpers ─────────────────────────────────────
1102
+ loading(label) {
1103
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1104
+ __privateGet(this, _controller).loading(label);
1105
+ }
1106
+ progress(percent) {
1107
+ __privateGet(this, _controller).progress(percent);
1108
+ }
1109
+ label(label) {
1110
+ __privateGet(this, _controller).label(label);
1111
+ }
1112
+ description(description) {
1113
+ __privateGet(this, _controller).description(description);
1114
+ }
1115
+ // ─── Queue ───────────────────────────────────────────────
1116
+ setQueues(labels) {
1117
+ __privateGet(this, _controller).setQueues(labels);
1118
+ }
1119
+ queue(labels) {
1120
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1121
+ __privateGet(this, _controller).queue(labels);
1122
+ }
1123
+ startQueue(label) {
1124
+ __privateGet(this, _controller).startQueue(label);
1125
+ }
1126
+ finishQueue(label) {
1127
+ __privateGet(this, _controller).finishQueue(label);
1128
+ }
1129
+ skipQueue(label) {
1130
+ __privateGet(this, _controller).skipQueue(label);
1131
+ }
1132
+ errorQueue(label) {
1133
+ __privateGet(this, _controller).errorQueue(label);
1134
+ }
1135
+ clearQueues() {
1136
+ __privateGet(this, _controller).clearQueues();
1137
+ }
1138
+ // ─── Steps ───────────────────────────────────────────────
1139
+ setSteps(labels) {
1140
+ __privateGet(this, _controller).setSteps(labels);
1141
+ }
1142
+ steps(labels) {
1143
+ __privateMethod(this, _DialogSingleton_instances, ensureElement_fn).call(this);
1144
+ __privateGet(this, _controller).steps(labels);
1145
+ }
1146
+ startStep(label) {
1147
+ __privateGet(this, _controller).startStep(label);
1148
+ }
1149
+ finishStep(label) {
1150
+ __privateGet(this, _controller).finishStep(label);
1151
+ }
1152
+ skipStep(label) {
1153
+ __privateGet(this, _controller).skipStep(label);
1154
+ }
1155
+ errorStep(label) {
1156
+ __privateGet(this, _controller).errorStep(label);
1157
+ }
1158
+ clearSteps() {
1159
+ __privateGet(this, _controller).clearSteps();
1160
+ }
1161
+ // ─── Advanced ────────────────────────────────────────────
1162
+ get controller() {
1163
+ return __privateGet(this, _controller);
1164
+ }
1165
+ };
1166
+ _controller = new WeakMap();
1167
+ _element = new WeakMap();
1168
+ _DialogSingleton_instances = new WeakSet();
1169
+ ensureElement_fn = function() {
1170
+ if (__privateGet(this, _element)) return;
1171
+ if (typeof document === "undefined") return;
1172
+ const el = document.createElement("overlay-dialog");
1173
+ el.controller = __privateGet(this, _controller);
1174
+ document.body.appendChild(el);
1175
+ __privateSet(this, _element, el);
754
1176
  };
1177
+ var dialog = new DialogSingleton();
755
1178
  // Annotate the CommonJS export names for ESM import in node:
756
1179
  0 && (module.exports = {
757
- ATTRIBUTE_ANIMATION,
758
- LoadingOverlay,
759
- Modal,
760
- TaskListOverlay,
761
- withLoading
1180
+ DialogController,
1181
+ OverlayDialog,
1182
+ dialog
762
1183
  });
763
1184
  //# sourceMappingURL=index.cjs.map