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