@growthcurve/flowmate-components 0.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.js ADDED
@@ -0,0 +1,653 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ FlowmateBanner: () => FlowmateBanner,
24
+ FlowmateCard: () => FlowmateCard,
25
+ FlowmateChecklist: () => FlowmateChecklist,
26
+ FlowmateFloatingBlocks: () => FlowmateFloatingBlocks,
27
+ FlowmateHint: () => FlowmateHint,
28
+ FlowmateModal: () => FlowmateModal,
29
+ FlowmateSidebar: () => FlowmateSidebar,
30
+ FlowmateTooltip: () => FlowmateTooltip,
31
+ setupComponents: () => setupComponents
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/styles.ts
36
+ var baseStyles = `
37
+ :host {
38
+ --fm-primary: var(--flowmate-primary, #6366f1);
39
+ --fm-bg: var(--flowmate-background, #ffffff);
40
+ --fm-text: var(--flowmate-foreground, #1a1a2e);
41
+ --fm-border: var(--flowmate-border, #e2e8f0);
42
+ --fm-radius: var(--flowmate-radius, 12px);
43
+ --fm-font: var(--flowmate-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
44
+ --fm-shadow: var(--flowmate-shadow, 0 4px 24px rgba(0,0,0,0.12));
45
+
46
+ font-family: var(--fm-font);
47
+ color: var(--fm-text);
48
+ box-sizing: border-box;
49
+ }
50
+
51
+ *, *::before, *::after { box-sizing: inherit; }
52
+
53
+ .fm-overlay {
54
+ position: fixed;
55
+ inset: 0;
56
+ background: rgba(0,0,0,0.5);
57
+ z-index: 10000;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ animation: fm-fade-in 0.2s ease;
62
+ }
63
+
64
+ .fm-card {
65
+ background: var(--fm-bg);
66
+ border-radius: var(--fm-radius);
67
+ box-shadow: var(--fm-shadow);
68
+ border: 1px solid var(--fm-border);
69
+ max-width: 480px;
70
+ width: 100%;
71
+ overflow: hidden;
72
+ }
73
+
74
+ .fm-header {
75
+ padding: 20px 24px 0;
76
+ }
77
+
78
+ .fm-title {
79
+ font-size: 18px;
80
+ font-weight: 600;
81
+ margin: 0 0 8px;
82
+ color: var(--fm-text);
83
+ }
84
+
85
+ .fm-body {
86
+ padding: 12px 24px;
87
+ font-size: 14px;
88
+ line-height: 1.6;
89
+ color: color-mix(in srgb, var(--fm-text) 80%, transparent);
90
+ }
91
+
92
+ .fm-actions {
93
+ padding: 16px 24px;
94
+ display: flex;
95
+ gap: 8px;
96
+ justify-content: flex-end;
97
+ }
98
+
99
+ .fm-btn {
100
+ padding: 8px 16px;
101
+ border-radius: calc(var(--fm-radius) * 0.6);
102
+ font-size: 14px;
103
+ font-weight: 500;
104
+ cursor: pointer;
105
+ border: none;
106
+ transition: opacity 0.15s;
107
+ font-family: var(--fm-font);
108
+ }
109
+
110
+ .fm-btn:hover { opacity: 0.85; }
111
+
112
+ .fm-btn-primary {
113
+ background: var(--fm-primary);
114
+ color: white;
115
+ }
116
+
117
+ .fm-btn-secondary {
118
+ background: transparent;
119
+ color: var(--fm-text);
120
+ border: 1px solid var(--fm-border);
121
+ }
122
+
123
+ .fm-close {
124
+ position: absolute;
125
+ top: 12px;
126
+ right: 12px;
127
+ background: none;
128
+ border: none;
129
+ cursor: pointer;
130
+ padding: 4px;
131
+ color: var(--fm-text);
132
+ opacity: 0.5;
133
+ font-size: 18px;
134
+ }
135
+
136
+ .fm-close:hover { opacity: 1; }
137
+
138
+ @keyframes fm-fade-in {
139
+ from { opacity: 0; }
140
+ to { opacity: 1; }
141
+ }
142
+
143
+ @keyframes fm-slide-up {
144
+ from { opacity: 0; transform: translateY(10px); }
145
+ to { opacity: 1; transform: translateY(0); }
146
+ }
147
+
148
+ @keyframes fm-slide-down {
149
+ from { opacity: 0; transform: translateY(-10px); }
150
+ to { opacity: 1; transform: translateY(0); }
151
+ }
152
+ `;
153
+
154
+ // src/base-element.ts
155
+ var FlowmateBaseElement = class extends HTMLElement {
156
+ constructor() {
157
+ super();
158
+ this.shadow = this.attachShadow({ mode: "open" });
159
+ }
160
+ createStyle(additionalCSS = "") {
161
+ const style = document.createElement("style");
162
+ style.textContent = baseStyles + additionalCSS;
163
+ return style;
164
+ }
165
+ emitAction(action, detail) {
166
+ this.dispatchEvent(new CustomEvent("flowmate-action", {
167
+ bubbles: true,
168
+ composed: true,
169
+ detail: { action, ...detail }
170
+ }));
171
+ }
172
+ emitDismiss() {
173
+ this.dispatchEvent(new CustomEvent("flowmate-dismiss", {
174
+ bubbles: true,
175
+ composed: true
176
+ }));
177
+ }
178
+ get blockData() {
179
+ const raw = this.getAttribute("data-block");
180
+ try {
181
+ return raw ? JSON.parse(raw) : {};
182
+ } catch {
183
+ return {};
184
+ }
185
+ }
186
+ get blockActions() {
187
+ const raw = this.getAttribute("data-actions");
188
+ try {
189
+ return raw ? JSON.parse(raw) : [];
190
+ } catch {
191
+ return [];
192
+ }
193
+ }
194
+ get blockTitle() {
195
+ return this.getAttribute("title") || "";
196
+ }
197
+ get blockBody() {
198
+ return this.getAttribute("body") || "";
199
+ }
200
+ get dismissible() {
201
+ return this.hasAttribute("dismissible");
202
+ }
203
+ };
204
+
205
+ // src/elements/flowmate-modal.ts
206
+ var FlowmateModal = class extends FlowmateBaseElement {
207
+ connectedCallback() {
208
+ const data = this.blockData;
209
+ const actions = this.blockActions;
210
+ this.shadow.appendChild(this.createStyle(`
211
+ :host { position: fixed; inset: 0; z-index: 10000; }
212
+ `));
213
+ const overlay = document.createElement("div");
214
+ overlay.className = "fm-overlay";
215
+ overlay.innerHTML = `
216
+ <div class="fm-card" style="position:relative; animation: fm-slide-up 0.25s ease;">
217
+ ${this.dismissible ? '<button class="fm-close">&times;</button>' : ""}
218
+ ${data.title ? `<div class="fm-header"><h2 class="fm-title">${data.title}</h2></div>` : ""}
219
+ ${data.body ? `<div class="fm-body">${data.body}</div>` : ""}
220
+ <div class="fm-actions">
221
+ ${actions.map((a) => `<button class="fm-btn ${a.variant === "secondary" ? "fm-btn-secondary" : "fm-btn-primary"}" data-key="${a.key}">${a.label}</button>`).join("")}
222
+ </div>
223
+ </div>
224
+ `;
225
+ overlay.addEventListener("click", (e) => {
226
+ const target = e.target;
227
+ if (target.classList.contains("fm-close")) this.emitDismiss();
228
+ if (target.classList.contains("fm-overlay")) this.emitDismiss();
229
+ if (target.dataset.key) this.emitAction(target.dataset.key);
230
+ });
231
+ this.shadow.appendChild(overlay);
232
+ }
233
+ disconnectedCallback() {
234
+ this.shadow.innerHTML = "";
235
+ }
236
+ };
237
+
238
+ // src/elements/flowmate-banner.ts
239
+ var FlowmateBanner = class extends FlowmateBaseElement {
240
+ connectedCallback() {
241
+ const data = this.blockData;
242
+ const actions = this.blockActions;
243
+ const position = data.position || "top";
244
+ this.shadow.appendChild(this.createStyle(`
245
+ :host {
246
+ position: fixed;
247
+ left: 0; right: 0;
248
+ ${position === "bottom" ? "bottom: 0;" : "top: 0;"}
249
+ z-index: 10000;
250
+ animation: ${position === "bottom" ? "fm-slide-up" : "fm-slide-down"} 0.3s ease;
251
+ }
252
+ .fm-banner {
253
+ background: var(--fm-primary);
254
+ color: white;
255
+ padding: 12px 24px;
256
+ display: flex;
257
+ align-items: center;
258
+ justify-content: space-between;
259
+ gap: 16px;
260
+ font-family: var(--fm-font);
261
+ }
262
+ .fm-banner-content { flex: 1; }
263
+ .fm-banner-title { font-weight: 600; font-size: 14px; }
264
+ .fm-banner-body { font-size: 13px; opacity: 0.9; margin-top: 2px; }
265
+ .fm-banner-actions { display: flex; gap: 8px; }
266
+ .fm-banner-btn {
267
+ padding: 6px 14px; border-radius: 6px; font-size: 13px;
268
+ font-weight: 500; cursor: pointer; border: 1px solid rgba(255,255,255,0.3);
269
+ background: rgba(255,255,255,0.15); color: white; font-family: var(--fm-font);
270
+ }
271
+ .fm-banner-btn:hover { background: rgba(255,255,255,0.25); }
272
+ .fm-banner-close {
273
+ background: none; border: none; color: white; cursor: pointer;
274
+ font-size: 18px; opacity: 0.7; padding: 4px;
275
+ }
276
+ .fm-banner-close:hover { opacity: 1; }
277
+ `));
278
+ const banner = document.createElement("div");
279
+ banner.className = "fm-banner";
280
+ banner.innerHTML = `
281
+ <div class="fm-banner-content">
282
+ ${data.title ? `<div class="fm-banner-title">${data.title}</div>` : ""}
283
+ ${data.body ? `<div class="fm-banner-body">${data.body}</div>` : ""}
284
+ </div>
285
+ <div class="fm-banner-actions">
286
+ ${actions.map((a) => `<button class="fm-banner-btn" data-key="${a.key}">${a.label}</button>`).join("")}
287
+ </div>
288
+ ${this.dismissible ? '<button class="fm-banner-close">&times;</button>' : ""}
289
+ `;
290
+ banner.addEventListener("click", (e) => {
291
+ const target = e.target;
292
+ if (target.classList.contains("fm-banner-close")) this.emitDismiss();
293
+ if (target.dataset.key) this.emitAction(target.dataset.key);
294
+ });
295
+ this.shadow.appendChild(banner);
296
+ }
297
+ disconnectedCallback() {
298
+ this.shadow.innerHTML = "";
299
+ }
300
+ };
301
+
302
+ // src/elements/flowmate-tooltip.ts
303
+ var FlowmateTooltip = class extends FlowmateBaseElement {
304
+ connectedCallback() {
305
+ const data = this.blockData;
306
+ const actions = this.blockActions;
307
+ const targetSelector = data.targetSelector || data.selector;
308
+ const placement = data.placement || "bottom";
309
+ this.shadow.appendChild(this.createStyle(`
310
+ :host { position: absolute; z-index: 10001; }
311
+ .fm-tooltip {
312
+ animation: fm-slide-up 0.2s ease;
313
+ max-width: 320px;
314
+ }
315
+ .fm-arrow {
316
+ position: absolute;
317
+ width: 10px; height: 10px;
318
+ background: var(--fm-bg);
319
+ border: 1px solid var(--fm-border);
320
+ transform: rotate(45deg);
321
+ }
322
+ `));
323
+ const tooltip = document.createElement("div");
324
+ tooltip.className = "fm-card fm-tooltip";
325
+ tooltip.innerHTML = `
326
+ ${this.dismissible ? '<button class="fm-close">&times;</button>' : ""}
327
+ ${data.title ? `<div class="fm-header"><h3 class="fm-title" style="font-size:15px;">${data.title}</h3></div>` : ""}
328
+ ${data.body ? `<div class="fm-body">${data.body}</div>` : ""}
329
+ ${actions.length ? `<div class="fm-actions">${actions.map((a) => `<button class="fm-btn ${a.variant === "secondary" ? "fm-btn-secondary" : "fm-btn-primary"}" data-key="${a.key}">${a.label}</button>`).join("")}</div>` : ""}
330
+ `;
331
+ tooltip.addEventListener("click", (e) => {
332
+ const target = e.target;
333
+ if (target.classList.contains("fm-close")) this.emitDismiss();
334
+ if (target.dataset.key) this.emitAction(target.dataset.key);
335
+ });
336
+ this.shadow.appendChild(tooltip);
337
+ if (targetSelector) {
338
+ requestAnimationFrame(() => {
339
+ const el = document.querySelector(targetSelector);
340
+ if (el) {
341
+ const rect = el.getBoundingClientRect();
342
+ const tooltipRect = tooltip.getBoundingClientRect();
343
+ let top = 0, left = 0;
344
+ switch (placement) {
345
+ case "top":
346
+ top = rect.top - tooltipRect.height - 12 + window.scrollY;
347
+ left = rect.left + rect.width / 2 - tooltipRect.width / 2 + window.scrollX;
348
+ break;
349
+ case "bottom":
350
+ top = rect.bottom + 12 + window.scrollY;
351
+ left = rect.left + rect.width / 2 - tooltipRect.width / 2 + window.scrollX;
352
+ break;
353
+ case "left":
354
+ top = rect.top + rect.height / 2 - tooltipRect.height / 2 + window.scrollY;
355
+ left = rect.left - tooltipRect.width - 12 + window.scrollX;
356
+ break;
357
+ case "right":
358
+ top = rect.top + rect.height / 2 - tooltipRect.height / 2 + window.scrollY;
359
+ left = rect.right + 12 + window.scrollX;
360
+ break;
361
+ }
362
+ this.style.top = `${Math.max(8, top)}px`;
363
+ this.style.left = `${Math.max(8, left)}px`;
364
+ }
365
+ });
366
+ }
367
+ }
368
+ disconnectedCallback() {
369
+ this.shadow.innerHTML = "";
370
+ }
371
+ };
372
+
373
+ // src/elements/flowmate-card.ts
374
+ var FlowmateCard = class extends FlowmateBaseElement {
375
+ connectedCallback() {
376
+ const data = this.blockData;
377
+ const actions = this.blockActions;
378
+ const position = data.position || "bottom-right";
379
+ const posMap = {
380
+ "bottom-right": "bottom: 24px; right: 24px;",
381
+ "bottom-left": "bottom: 24px; left: 24px;",
382
+ "top-right": "top: 24px; right: 24px;",
383
+ "top-left": "top: 24px; left: 24px;"
384
+ };
385
+ this.shadow.appendChild(this.createStyle(`
386
+ :host { position: fixed; ${posMap[position] || posMap["bottom-right"]} z-index: 10000; }
387
+ .fm-card { animation: fm-slide-up 0.25s ease; max-width: 360px; position: relative; }
388
+ `));
389
+ const card = document.createElement("div");
390
+ card.className = "fm-card";
391
+ card.innerHTML = `
392
+ ${this.dismissible ? '<button class="fm-close">&times;</button>' : ""}
393
+ ${data.title ? `<div class="fm-header"><h3 class="fm-title">${data.title}</h3></div>` : ""}
394
+ ${data.body ? `<div class="fm-body">${data.body}</div>` : ""}
395
+ ${actions.length ? `<div class="fm-actions">${actions.map((a) => `<button class="fm-btn ${a.variant === "secondary" ? "fm-btn-secondary" : "fm-btn-primary"}" data-key="${a.key}">${a.label}</button>`).join("")}</div>` : ""}
396
+ `;
397
+ card.addEventListener("click", (e) => {
398
+ const target = e.target;
399
+ if (target.classList.contains("fm-close")) this.emitDismiss();
400
+ if (target.dataset.key) this.emitAction(target.dataset.key);
401
+ });
402
+ this.shadow.appendChild(card);
403
+ }
404
+ disconnectedCallback() {
405
+ this.shadow.innerHTML = "";
406
+ }
407
+ };
408
+
409
+ // src/elements/flowmate-hint.ts
410
+ var FlowmateHint = class extends FlowmateBaseElement {
411
+ connectedCallback() {
412
+ const data = this.blockData;
413
+ const targetSelector = data.targetSelector || data.selector;
414
+ this.shadow.appendChild(this.createStyle(`
415
+ :host { position: absolute; z-index: 10001; pointer-events: none; }
416
+ .fm-hint {
417
+ width: 16px; height: 16px;
418
+ border-radius: 50%;
419
+ background: var(--fm-primary);
420
+ position: relative;
421
+ pointer-events: auto;
422
+ cursor: pointer;
423
+ }
424
+ .fm-hint::after {
425
+ content: '';
426
+ position: absolute;
427
+ inset: -4px;
428
+ border-radius: 50%;
429
+ border: 2px solid var(--fm-primary);
430
+ animation: fm-pulse 2s infinite;
431
+ }
432
+ @keyframes fm-pulse {
433
+ 0%, 100% { opacity: 1; transform: scale(1); }
434
+ 50% { opacity: 0.4; transform: scale(1.5); }
435
+ }
436
+ `));
437
+ const hint = document.createElement("div");
438
+ hint.className = "fm-hint";
439
+ hint.addEventListener("click", () => this.emitAction("hint-click"));
440
+ this.shadow.appendChild(hint);
441
+ if (targetSelector) {
442
+ requestAnimationFrame(() => {
443
+ const el = document.querySelector(targetSelector);
444
+ if (el) {
445
+ const rect = el.getBoundingClientRect();
446
+ this.style.top = `${rect.top - 8 + window.scrollY}px`;
447
+ this.style.left = `${rect.right - 8 + window.scrollX}px`;
448
+ }
449
+ });
450
+ }
451
+ }
452
+ disconnectedCallback() {
453
+ this.shadow.innerHTML = "";
454
+ }
455
+ };
456
+
457
+ // src/elements/flowmate-sidebar.ts
458
+ var FlowmateSidebar = class extends FlowmateBaseElement {
459
+ connectedCallback() {
460
+ const data = this.blockData;
461
+ const actions = this.blockActions;
462
+ const side = data.side || "right";
463
+ const width = data.width || "380px";
464
+ this.shadow.appendChild(this.createStyle(`
465
+ :host { position: fixed; inset: 0; z-index: 10000; }
466
+ .fm-sidebar-overlay {
467
+ position: fixed; inset: 0;
468
+ background: rgba(0,0,0,0.4);
469
+ animation: fm-fade-in 0.2s ease;
470
+ }
471
+ .fm-sidebar-panel {
472
+ position: fixed; top: 0; bottom: 0;
473
+ ${side === "left" ? "left: 0;" : "right: 0;"}
474
+ width: ${width};
475
+ max-width: 90vw;
476
+ background: var(--fm-bg);
477
+ box-shadow: var(--fm-shadow);
478
+ display: flex;
479
+ flex-direction: column;
480
+ animation: fm-slide-${side === "left" ? "right" : "left"} 0.3s ease;
481
+ }
482
+ .fm-sidebar-header {
483
+ padding: 20px 24px;
484
+ border-bottom: 1px solid var(--fm-border);
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: space-between;
488
+ }
489
+ .fm-sidebar-body { padding: 24px; flex: 1; overflow-y: auto; font-size: 14px; line-height: 1.6; }
490
+ .fm-sidebar-footer { padding: 16px 24px; border-top: 1px solid var(--fm-border); display: flex; gap: 8px; justify-content: flex-end; }
491
+ @keyframes fm-slide-right { from { transform: translateX(-100%); } to { transform: translateX(0); } }
492
+ @keyframes fm-slide-left { from { transform: translateX(100%); } to { transform: translateX(0); } }
493
+ `));
494
+ const container = document.createElement("div");
495
+ container.innerHTML = `
496
+ <div class="fm-sidebar-overlay"></div>
497
+ <div class="fm-sidebar-panel">
498
+ <div class="fm-sidebar-header">
499
+ <h2 class="fm-title" style="margin:0;">${data.title || ""}</h2>
500
+ <button class="fm-close" style="position:static;">&times;</button>
501
+ </div>
502
+ ${data.body ? `<div class="fm-sidebar-body">${data.body}</div>` : ""}
503
+ ${actions.length ? `<div class="fm-sidebar-footer">${actions.map((a) => `<button class="fm-btn ${a.variant === "secondary" ? "fm-btn-secondary" : "fm-btn-primary"}" data-key="${a.key}">${a.label}</button>`).join("")}</div>` : ""}
504
+ </div>
505
+ `;
506
+ container.addEventListener("click", (e) => {
507
+ const target = e.target;
508
+ if (target.classList.contains("fm-sidebar-overlay") || target.classList.contains("fm-close")) this.emitDismiss();
509
+ if (target.dataset.key) this.emitAction(target.dataset.key);
510
+ });
511
+ this.shadow.appendChild(container);
512
+ }
513
+ disconnectedCallback() {
514
+ this.shadow.innerHTML = "";
515
+ }
516
+ };
517
+
518
+ // src/elements/flowmate-checklist.ts
519
+ var FlowmateChecklist = class extends FlowmateBaseElement {
520
+ constructor() {
521
+ super(...arguments);
522
+ this.checkedItems = /* @__PURE__ */ new Set();
523
+ }
524
+ connectedCallback() {
525
+ const data = this.blockData;
526
+ const items = data.items || [];
527
+ this.shadow.appendChild(this.createStyle(`
528
+ :host { position: fixed; bottom: 24px; right: 24px; z-index: 10000; }
529
+ .fm-checklist {
530
+ width: 340px; max-height: 480px;
531
+ animation: fm-slide-up 0.25s ease;
532
+ overflow: hidden; display: flex; flex-direction: column;
533
+ }
534
+ .fm-checklist-header {
535
+ padding: 16px 20px;
536
+ border-bottom: 1px solid var(--fm-border);
537
+ display: flex; align-items: center; justify-content: space-between;
538
+ }
539
+ .fm-checklist-items { padding: 8px 0; overflow-y: auto; flex: 1; }
540
+ .fm-checklist-item {
541
+ padding: 12px 20px;
542
+ display: flex; align-items: flex-start; gap: 12px;
543
+ cursor: pointer; transition: background 0.1s;
544
+ }
545
+ .fm-checklist-item:hover { background: color-mix(in srgb, var(--fm-primary) 5%, transparent); }
546
+ .fm-check {
547
+ width: 20px; height: 20px; border-radius: 50%;
548
+ border: 2px solid var(--fm-border); flex-shrink: 0;
549
+ display: flex; align-items: center; justify-content: center;
550
+ transition: all 0.2s;
551
+ }
552
+ .fm-check.checked {
553
+ background: var(--fm-primary); border-color: var(--fm-primary);
554
+ }
555
+ .fm-check.checked::after { content: '\u2713'; color: white; font-size: 12px; }
556
+ .fm-item-title { font-size: 14px; font-weight: 500; }
557
+ .fm-item-title.checked { text-decoration: line-through; opacity: 0.5; }
558
+ .fm-item-desc { font-size: 12px; opacity: 0.6; margin-top: 2px; }
559
+ .fm-progress { height: 4px; background: var(--fm-border); border-radius: 2px; margin: 0 20px 12px; }
560
+ .fm-progress-bar { height: 100%; background: var(--fm-primary); border-radius: 2px; transition: width 0.3s; }
561
+ `));
562
+ this.render(data, items);
563
+ }
564
+ render(data, items) {
565
+ const existing = this.shadow.querySelector(".fm-checklist");
566
+ if (existing) existing.remove();
567
+ const pct = items.length ? this.checkedItems.size / items.length * 100 : 0;
568
+ const el = document.createElement("div");
569
+ el.className = "fm-card fm-checklist";
570
+ el.innerHTML = `
571
+ <div class="fm-checklist-header">
572
+ <div>
573
+ <div class="fm-title" style="font-size:15px; margin:0;">${data.title || "Getting Started"}</div>
574
+ <div style="font-size:12px; opacity:0.6; margin-top:2px;">${this.checkedItems.size}/${items.length} complete</div>
575
+ </div>
576
+ ${data.dismissible !== false ? '<button class="fm-close" style="position:static;">&times;</button>' : ""}
577
+ </div>
578
+ <div class="fm-progress"><div class="fm-progress-bar" style="width:${pct}%"></div></div>
579
+ <div class="fm-checklist-items">
580
+ ${items.map((item, i) => {
581
+ const checked = this.checkedItems.has(i);
582
+ return `<div class="fm-checklist-item" data-index="${i}">
583
+ <div class="fm-check ${checked ? "checked" : ""}"></div>
584
+ <div>
585
+ <div class="fm-item-title ${checked ? "checked" : ""}">${item.title}</div>
586
+ ${item.description ? `<div class="fm-item-desc">${item.description}</div>` : ""}
587
+ </div>
588
+ </div>`;
589
+ }).join("")}
590
+ </div>
591
+ `;
592
+ el.addEventListener("click", (e) => {
593
+ const target = e.target.closest(".fm-checklist-item");
594
+ if (target) {
595
+ const idx = parseInt(target.dataset.index || "0");
596
+ if (this.checkedItems.has(idx)) this.checkedItems.delete(idx);
597
+ else this.checkedItems.add(idx);
598
+ this.render(data, items);
599
+ if (this.checkedItems.size === items.length) {
600
+ setTimeout(() => this.emitAction("complete"), 500);
601
+ }
602
+ }
603
+ if (e.target.classList.contains("fm-close")) this.emitDismiss();
604
+ });
605
+ this.shadow.appendChild(el);
606
+ }
607
+ disconnectedCallback() {
608
+ this.shadow.innerHTML = "";
609
+ }
610
+ };
611
+
612
+ // src/elements/flowmate-floating-blocks.ts
613
+ var FlowmateFloatingBlocks = class extends FlowmateBaseElement {
614
+ connectedCallback() {
615
+ this.shadow.appendChild(this.createStyle(`
616
+ :host { display: contents; }
617
+ `));
618
+ const slot = document.createElement("slot");
619
+ this.shadow.appendChild(slot);
620
+ }
621
+ };
622
+
623
+ // src/setup.ts
624
+ var registry = {
625
+ "flowmate-modal": FlowmateModal,
626
+ "flowmate-banner": FlowmateBanner,
627
+ "flowmate-tooltip": FlowmateTooltip,
628
+ "flowmate-card": FlowmateCard,
629
+ "flowmate-hint": FlowmateHint,
630
+ "flowmate-sidebar": FlowmateSidebar,
631
+ "flowmate-checklist": FlowmateChecklist,
632
+ "flowmate-floating-blocks": FlowmateFloatingBlocks
633
+ };
634
+ function setupComponents(options) {
635
+ const components = { ...registry, ...options?.components || {} };
636
+ for (const [name, constructor] of Object.entries(components)) {
637
+ if (!customElements.get(name)) {
638
+ customElements.define(name, constructor);
639
+ }
640
+ }
641
+ }
642
+ // Annotate the CommonJS export names for ESM import in node:
643
+ 0 && (module.exports = {
644
+ FlowmateBanner,
645
+ FlowmateCard,
646
+ FlowmateChecklist,
647
+ FlowmateFloatingBlocks,
648
+ FlowmateHint,
649
+ FlowmateModal,
650
+ FlowmateSidebar,
651
+ FlowmateTooltip,
652
+ setupComponents
653
+ });