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