zynaui 0.1.0-beta.1

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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +491 -0
  3. package/dist/base-CaP9Us8w.js +89 -0
  4. package/dist/base-DyLOr2iL.cjs +1 -0
  5. package/dist/charts/lollipop.cjs +1 -0
  6. package/dist/charts/lollipop.js +38 -0
  7. package/dist/charts/nightingale.cjs +1 -0
  8. package/dist/charts/nightingale.js +35 -0
  9. package/dist/charts/orbital.cjs +1 -0
  10. package/dist/charts/orbital.js +48 -0
  11. package/dist/charts/timeline.cjs +1 -0
  12. package/dist/charts/timeline.js +32 -0
  13. package/dist/charts/waffle.cjs +1 -0
  14. package/dist/charts/waffle.js +27 -0
  15. package/dist/genres.cjs +1 -0
  16. package/dist/genres.js +7 -0
  17. package/dist/index-6PLXN0mK.js +313 -0
  18. package/dist/index-CznVizeu.cjs +1 -0
  19. package/dist/react.cjs +1 -0
  20. package/dist/react.js +67 -0
  21. package/dist/zyna-charts-stub.cjs +5 -0
  22. package/dist/zyna-charts.cjs +1 -0
  23. package/dist/zyna-charts.iife.js +1 -0
  24. package/dist/zyna-charts.js +12 -0
  25. package/dist/zyna-plugin.cjs +1 -0
  26. package/dist/zyna-plugin.js +949 -0
  27. package/dist/zynaui.css +1836 -0
  28. package/package.json +137 -0
  29. package/src/charts/base.js +159 -0
  30. package/src/charts/index.js +6 -0
  31. package/src/charts/lollipop.js +132 -0
  32. package/src/charts/nightingale.js +118 -0
  33. package/src/charts/orbital.js +176 -0
  34. package/src/charts/timeline.js +128 -0
  35. package/src/charts/waffle.js +75 -0
  36. package/src/index.js +6 -0
  37. package/src/plugin/components/alert/alert.js +222 -0
  38. package/src/plugin/components/badge/badge.js +254 -0
  39. package/src/plugin/components/btn/btn.js +289 -0
  40. package/src/plugin/components/card/card.js +308 -0
  41. package/src/plugin/components/index.js +14 -0
  42. package/src/plugin/genres/cyberpunk.js +163 -0
  43. package/src/plugin/genres/define.js +59 -0
  44. package/src/plugin/genres/index.js +49 -0
  45. package/src/plugin/genres/ops.js +144 -0
  46. package/src/plugin/index.js +81 -0
  47. package/src/plugin/tokens.js +105 -0
  48. package/src/plugin/utils/motion.js +30 -0
  49. package/src/plugin/utils/prefix.js +54 -0
  50. package/src/plugin/utils/shapes.js +48 -0
  51. package/src/react/index.js +84 -0
  52. package/types/charts.d.ts +121 -0
  53. package/types/genres.d.ts +98 -0
  54. package/types/index.d.ts +65 -0
  55. package/types/react.d.ts +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Binary Tech Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,491 @@
1
+ # Zyna UI
2
+
3
+ > Tailwind CSS component library + D3-powered chart Web Components
4
+ > Built by [Binary Tech Ltd](https://binary.ly) · Open source · MIT License
5
+
6
+ [![npm version](https://img.shields.io/npm/v/zynaui)](https://www.npmjs.com/package/zynaui)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Website](https://img.shields.io/badge/website-zyna.dev-gold)](https://zyna.dev)
9
+
10
+ ---
11
+
12
+ ## What's included
13
+
14
+ | Package | Description |
15
+ |---------|-------------|
16
+ | **Tailwind plugin** | Semantic classes: `.btn`, `.card`, `.badge`, `.alert` |
17
+ | **Chart Web Components** | `<zyna-waffle>`, `<zyna-timeline>`, `<zyna-nightingale>`, `<zyna-lollipop>`, `<zyna-orbital>` |
18
+
19
+ Framework-agnostic — works in React, Vue, Svelte, Blade, or plain HTML.
20
+
21
+ ---
22
+
23
+ ## UI Components (Tailwind Plugin)
24
+
25
+ ### Installation
26
+
27
+ ```bash
28
+ npm install zynaui tailwindcss
29
+ ```
30
+
31
+ **Tailwind v3** — `tailwind.config.js`:
32
+
33
+ ```js
34
+ module.exports = {
35
+ content: ['./src/**/*.{html,js,jsx,ts,tsx,vue,svelte}'],
36
+ plugins: [require('zynaui')],
37
+ }
38
+ ```
39
+
40
+ **Tailwind v4** — `app.css`:
41
+
42
+ ```css
43
+ @import "tailwindcss";
44
+ @plugin "zynaui";
45
+ ```
46
+
47
+ ---
48
+
49
+ ### Plugin options
50
+
51
+ #### `prefix` — avoid class name conflicts
52
+
53
+ If another library in your project already uses `.btn`, `.card`, `.badge`, or `.alert`, add a prefix:
54
+
55
+ **Tailwind v3:**
56
+
57
+ ```js
58
+ plugins: [require('zynaui')({ prefix: 'z-' })]
59
+ // → .z-btn, .z-btn-primary, .z-card, .z-badge, .z-alert …
60
+ ```
61
+
62
+ **Tailwind v4:**
63
+
64
+ ```css
65
+ @plugin "zynaui" {
66
+ prefix: z-;
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ### Tailwind utilities
73
+
74
+ ZynaUI extends the Tailwind theme with semantic color and radius tokens, so you can use them as native utility classes:
75
+
76
+ ```html
77
+ <!-- Status colors -->
78
+ <span class="text-zyna-success">Operational</span>
79
+ <span class="text-zyna-danger">Critical</span>
80
+ <span class="text-zyna-warning">Degraded</span>
81
+ <span class="text-zyna-info">Updating</span>
82
+ <span class="text-zyna-muted">Offline</span>
83
+
84
+ <!-- Brand color -->
85
+ <span class="text-zyna">Gold accent</span>
86
+
87
+ <!-- Corner radius (respects active genre) -->
88
+ <div class="rounded-zyna-sm">…</div>
89
+ <div class="rounded-zyna">…</div>
90
+ <div class="rounded-zyna-lg">…</div>
91
+ ```
92
+
93
+ These resolve to CSS variables (e.g. `text-zyna-success` → `color: var(--z-color-success)`) and update automatically when the active genre changes.
94
+
95
+ ---
96
+
97
+ ### Buttons
98
+
99
+ ```html
100
+ <button class="btn btn-primary">Primary</button>
101
+ <button class="btn btn-secondary">Secondary</button>
102
+ <button class="btn btn-ghost">Ghost</button>
103
+ <button class="btn btn-danger">Danger</button>
104
+
105
+ <!-- Sizes -->
106
+ <button class="btn btn-primary btn-sm">Small</button>
107
+ <button class="btn btn-primary btn-lg">Large</button>
108
+
109
+ <!-- Icon button -->
110
+ <button class="btn btn-primary btn-icon">
111
+ <svg>…</svg>
112
+ </button>
113
+ ```
114
+
115
+ **Custom button variant** — set CSS variables, no plugin changes needed:
116
+
117
+ ```css
118
+ .btn-plasma {
119
+ --btn-bg: rgba(139, 0, 255, 0.38);
120
+ --btn-color: #BF5FFF;
121
+ --btn-filter: drop-shadow(0 0 8px rgba(139,0,255,0.45));
122
+ --btn-scan-color: rgba(139, 0, 255, 0.18);
123
+ --btn-hover-filter: drop-shadow(0 0 22px rgba(139,0,255,1)) brightness(1.10);
124
+ --btn-hover-text-shadow: 0 0 16px rgba(200,100,255,0.7);
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ### Badges
131
+
132
+ ```html
133
+ <span class="badge badge-primary">New</span>
134
+ <span class="badge badge-success badge-pulse">Active</span>
135
+ <span class="badge badge-danger">Error</span>
136
+ <span class="badge badge-warning">Pending</span>
137
+ <span class="badge badge-info">Info</span>
138
+ <span class="badge badge-neutral">Draft</span>
139
+
140
+ <!-- Large -->
141
+ <span class="badge badge-primary badge-lg">Featured</span>
142
+ ```
143
+
144
+ **Custom badge variant:**
145
+
146
+ ```css
147
+ .badge-plasma {
148
+ --badge-bg: rgba(139, 0, 255, 0.10);
149
+ --badge-color: #BF5FFF;
150
+ --badge-glow: drop-shadow(0 0 5px rgba(139,0,255,0.45))
151
+ drop-shadow(0 0 14px rgba(139,0,255,0.14));
152
+ }
153
+ ```
154
+
155
+ ---
156
+
157
+ ### Cards
158
+
159
+ ```html
160
+ <div class="card">
161
+ <div class="card-header">System Status</div>
162
+ <div class="card-body">
163
+ <p class="card-title">Card Title</p>
164
+ <p class="card-subtitle">Supporting subtitle</p>
165
+ <p>Body content here.</p>
166
+ </div>
167
+ <div class="card-footer">Footer</div>
168
+ </div>
169
+
170
+ <!-- Variants -->
171
+ <div class="card card-dark">…</div>
172
+ <div class="card card-glow">…</div>
173
+ <div class="card card-compact">…</div>
174
+ ```
175
+
176
+ **Custom card variant:**
177
+
178
+ ```css
179
+ .card-cyber {
180
+ --card-gradient: linear-gradient(145deg, rgba(0,20,30,0.97) 0%, rgba(0,10,18,0.97) 100%);
181
+ --card-border-color: rgba(0,212,255,0.22);
182
+ --card-bracket-color: rgba(0,212,255,0.55);
183
+ --card-bar-gradient: linear-gradient(90deg, transparent 0%, rgba(0,212,255,0.55) 25%, rgba(0,212,255,0.55) 75%, transparent 100%);
184
+ --card-glow-lo: rgba(0,212,255,0.12);
185
+ --card-glow-hi: rgba(0,212,255,0.26);
186
+ --card-animation: zyna-card-pulse 4s ease-in-out infinite;
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ### Alerts
193
+
194
+ ```html
195
+ <div class="alert alert-success">
196
+ <p class="alert-title">Success</p>
197
+ <p>Your changes have been saved.</p>
198
+ </div>
199
+
200
+ <div class="alert alert-danger">…</div>
201
+ <div class="alert alert-warning">…</div>
202
+ <div class="alert alert-info">…</div>
203
+ ```
204
+
205
+ **Custom alert variant:**
206
+
207
+ ```css
208
+ .alert-plasma {
209
+ --alert-bar-color: #BF5FFF;
210
+ --alert-bg: rgba(139, 0, 255, 0.055);
211
+ --alert-color: rgba(191, 95, 255, 0.88);
212
+ --alert-shadow: 0 0 30px rgba(139,0,255,0.08),
213
+ inset 4px 0 18px rgba(139,0,255,0.05);
214
+ --alert-title-shadow: 0 0 12px rgba(191,95,255,0.65);
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Chart Web Components
221
+
222
+ ### React & Next.js — typed wrapper components
223
+
224
+ Install the wrapper and get typed React components that accept native arrays and numbers:
225
+
226
+ ```bash
227
+ npm install zynaui
228
+ ```
229
+
230
+ ```tsx
231
+ import { ZynaWaffle, ZynaTimeline, ZynaNightingale, ZynaLollipop, ZynaOrbital } from 'zynaui/react'
232
+
233
+ export default function Charts() {
234
+ const data = [
235
+ { label: 'Food', value: 35, color: '#C9A84C' },
236
+ { label: 'Shelter', value: 25, color: '#009EDB', outline: true },
237
+ ]
238
+ return <ZynaWaffle data={data} cols={10} gap={3} />
239
+ }
240
+ ```
241
+
242
+ The `'use client'` directive is baked into the package — works with Next.js App Router out of the box. The IIFE bundle is inlined, so no file copying or `next/script` setup is needed.
243
+
244
+ ---
245
+
246
+ ### Via bundler (Vue / Svelte / Astro)
247
+
248
+ ```js
249
+ import 'zynaui/charts'
250
+ ```
251
+
252
+ ### Individual chart imports
253
+
254
+ ```js
255
+ import 'zynaui/charts/waffle'
256
+ import 'zynaui/charts/timeline'
257
+ import 'zynaui/charts/nightingale'
258
+ import 'zynaui/charts/lollipop'
259
+ import 'zynaui/charts/orbital'
260
+ ```
261
+
262
+ ### Via CDN / Vanilla HTML (no bundler)
263
+
264
+ Link the pre-compiled CSS and load the IIFE bundle — no build step needed:
265
+
266
+ ```html
267
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/zynaui/dist/zynaui.css" />
268
+ <script src="https://cdn.jsdelivr.net/npm/zynaui/dist/zyna-charts.iife.js"></script>
269
+ ```
270
+
271
+ Or if installed via npm:
272
+
273
+ ```html
274
+ <link rel="stylesheet" href="node_modules/zynaui/dist/zynaui.css" />
275
+ <script src="node_modules/zynaui/dist/zyna-charts.iife.js"></script>
276
+ ```
277
+
278
+ ---
279
+
280
+ ### `<zyna-waffle>`
281
+
282
+ Waffle chart — square-grid cells, each filled or outline-only.
283
+
284
+ | Attribute | Type | Default | Description |
285
+ |-----------|------|---------|-------------|
286
+ | `data` | JSON array | `[]` | `[{ label, value, color?, outline? }]` |
287
+ | `color` | hex | `#C9A84C` | Fallback cell color |
288
+ | `cols` | number | `10` | Grid columns |
289
+ | `gap` | number | `3` | Gap between cells (px) |
290
+
291
+ ```html
292
+ <zyna-waffle
293
+ data='[
294
+ { "label": "UK", "value": 24, "color": "#1A3A6B" },
295
+ { "label": "US", "value": 28, "color": "#009EDB" },
296
+ { "label": "EU", "value": 20, "color": "#4A6741", "outline": true }
297
+ ]'
298
+ ></zyna-waffle>
299
+ ```
300
+
301
+ ---
302
+
303
+ ### `<zyna-timeline>`
304
+
305
+ Proportional-circle timeline — bubble area encodes value.
306
+
307
+ | Attribute | Type | Default | Description |
308
+ |-----------|------|---------|-------------|
309
+ | `data` | JSON array | `[]` | `[{ label, value, note? }]` |
310
+ | `color` | hex | `#C9A84C` | Accent color for highlighted item |
311
+ | `theme` | `dark`/`light` | `dark` | Color theme |
312
+ | `highlight` | string | highest value | Label of the item to emphasize |
313
+
314
+ ```html
315
+ <zyna-timeline
316
+ data='[
317
+ { "label": "2019", "value": 120 },
318
+ { "label": "2020", "value": 95, "note": "COVID" },
319
+ { "label": "2021", "value": 180 },
320
+ { "label": "2022", "value": 210 }
321
+ ]'
322
+ highlight="2022"
323
+ ></zyna-timeline>
324
+ ```
325
+
326
+ ---
327
+
328
+ ### `<zyna-nightingale>`
329
+
330
+ Nightingale (rose) chart — sector radius encodes value.
331
+
332
+ | Attribute | Type | Default | Description |
333
+ |-----------|------|---------|-------------|
334
+ | `data` | JSON array | `[]` | `[{ label, value, color? }]` |
335
+ | `color` | hex | `#C9A84C` | Fallback sector color |
336
+ | `theme` | `dark`/`light` | `dark` | Color theme |
337
+
338
+ ```html
339
+ <zyna-nightingale
340
+ data='[
341
+ { "label": "Food", "value": 42, "color": "#C9A84C" },
342
+ { "label": "Shelter", "value": 31, "color": "#009EDB" },
343
+ { "label": "Education", "value": 18, "color": "#00FFB2" },
344
+ { "label": "Health", "value": 27, "color": "#FF3366" }
345
+ ]'
346
+ ></zyna-nightingale>
347
+ ```
348
+
349
+ ---
350
+
351
+ ### `<zyna-lollipop>`
352
+
353
+ Horizontal lollipop chart — line + circle encodes value.
354
+
355
+ | Attribute | Type | Default | Description |
356
+ |-----------|------|---------|-------------|
357
+ | `data` | JSON array | `[]` | `[{ label, value }]` sorted descending recommended |
358
+ | `color` | hex | `#C9A84C` | Accent color for the top item |
359
+ | `theme` | `dark`/`light` | `dark` | Color theme |
360
+
361
+ ```html
362
+ <zyna-lollipop
363
+ data='[
364
+ { "label": "Libya", "value": 820 },
365
+ { "label": "Sudan", "value": 610 },
366
+ { "label": "Syria", "value": 490 },
367
+ { "label": "Yemen", "value": 380 }
368
+ ]'
369
+ ></zyna-lollipop>
370
+ ```
371
+
372
+ ---
373
+
374
+ ### `<zyna-orbital>`
375
+
376
+ Concentric arc chart — each ring filled as a proportion of a full circle.
377
+
378
+ | Attribute | Type | Default | Description |
379
+ |-----------|------|---------|-------------|
380
+ | `data` | JSON array | `[]` | `[{ label, value, color? }]` — `value` is `0`–`1` |
381
+ | `color` | hex | `#C9A84C` | Fallback ring color |
382
+ | `theme` | `dark`/`light` | `dark` | Color theme |
383
+
384
+ ```html
385
+ <zyna-orbital
386
+ data='[
387
+ { "label": "Delivered", "value": 0.78, "color": "#C9A84C" },
388
+ { "label": "In Transit","value": 0.55, "color": "#009EDB" },
389
+ { "label": "Planned", "value": 0.32, "color": "#00FFB2" }
390
+ ]'
391
+ ></zyna-orbital>
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Custom genres
397
+
398
+ Create a fully custom visual paradigm with `defineGenre`:
399
+
400
+ ```js
401
+ import { defineGenre, registerGenre } from 'zynaui/genres'
402
+
403
+ const aurora = defineGenre({
404
+ name: 'Aurora',
405
+ palette: { brand: '#BF5FFF' },
406
+ tokens: {
407
+ '--zyna': '#BF5FFF',
408
+ '--z-ease-enter': 'cubic-bezier(0.34, 1.56, 0.64, 1)',
409
+ '--z-duration-fast': '0.14s',
410
+ },
411
+ styles: {
412
+ 'html[data-genre="aurora"]': {
413
+ '--z-btn-clip': 'inset(0)',
414
+ '--z-badge-clip': 'inset(0 round 4px)',
415
+ },
416
+ },
417
+ })
418
+
419
+ registerGenre(aurora)
420
+ ```
421
+
422
+ Activate at runtime:
423
+
424
+ ```js
425
+ document.documentElement.setAttribute('data-genre', 'aurora')
426
+ ```
427
+
428
+ > **Note:** Genre structural styles (`styles`) are compiled into `zynaui.css` at Tailwind build time. Register custom genres before your build step runs so their rules are included in the output.
429
+
430
+ ---
431
+
432
+ ## Build
433
+
434
+ ```bash
435
+ npm install
436
+ npm run build
437
+ ```
438
+
439
+ Outputs:
440
+
441
+ | File | Format | Use case |
442
+ |------|--------|----------|
443
+ | `dist/zyna-plugin.cjs` | CommonJS | Tailwind config `require()` |
444
+ | `dist/zyna-plugin.js` | ESM | Bundler import |
445
+ | `dist/genres.js` | ESM | `import { defineGenre } from 'zynaui/genres'` |
446
+ | `dist/zyna-charts.js` | ESM | Bundler `import 'zynaui/charts'` |
447
+ | `dist/zyna-charts.iife.js` | IIFE | `<script src>` with no bundler |
448
+ | `dist/zyna-charts-stub.cjs` | CJS stub | SSR environments (auto-selected) |
449
+ | `dist/react.js` | ESM | `import { ZynaWaffle } from 'zynaui/react'` |
450
+ | `dist/zynaui.css` | CSS | Pre-compiled CSS for CDN / vanilla HTML |
451
+
452
+ ```bash
453
+ # Build only the Tailwind plugin
454
+ npm run build:lib
455
+
456
+ # Build only the IIFE bundle (CDN)
457
+ npm run build:iife
458
+
459
+ # Build only the docs CSS
460
+ npm run build:css
461
+ ```
462
+
463
+ ---
464
+
465
+ ## CSS Variable API
466
+
467
+ All components are driven entirely by CSS custom properties. Create custom variants without touching plugin source:
468
+
469
+ ```css
470
+ /* A custom button — just set variables */
471
+ .btn-ocean {
472
+ --btn-bg: linear-gradient(135deg, #006994 0%, #003d5b 100%);
473
+ --btn-color: #7ED8F6;
474
+ --btn-filter: drop-shadow(0 0 8px rgba(0,105,148,0.5));
475
+ --btn-hover-filter: drop-shadow(0 0 22px rgba(0,185,255,0.9)) brightness(1.12);
476
+ }
477
+ ```
478
+
479
+ See the JSDoc at the top of each component file in `src/plugin/components/` for the full variable reference.
480
+
481
+ ---
482
+
483
+ ## Credits
484
+
485
+ The semantic component class naming convention (`.btn`, `.btn-primary`, `.card`, `.badge`, `.alert`, etc.) is inspired by [DaisyUI](https://daisyui.com) by Pouya Saadeghi — the leading semantic Tailwind component library. Zyna UI takes that convention and applies a dark, HUD-style aesthetic with CSS custom properties, clip-path geometry, and D3-powered chart Web Components.
486
+
487
+ ---
488
+
489
+ ## License
490
+
491
+ MIT © [Binary Tech Ltd](https://binary.ly)
@@ -0,0 +1,89 @@
1
+ class l extends HTMLElement {
2
+ connectedCallback() {
3
+ this._lastW = 0, this._rafId = null, this._timerId = null, this._genreHandler = () => this._render(), window.addEventListener("zyna-genre", this._genreHandler), this._ro = new ResizeObserver((t) => {
4
+ var r;
5
+ const e = ((r = t[0]) == null ? void 0 : r.contentRect.width) ?? this.clientWidth;
6
+ if (Math.abs(e - this._lastW) < 3) return;
7
+ const i = this._lastW === 0;
8
+ this._lastW = e, this._rafId && cancelAnimationFrame(this._rafId), this._rafId = requestAnimationFrame(() => {
9
+ this._rafId = null, clearTimeout(this._timerId), i ? this._render() : this._timerId = setTimeout(() => this._render(), 150);
10
+ });
11
+ }), this._ro.observe(this), requestAnimationFrame(() => {
12
+ this._lastW === 0 && (this._lastW = this.clientWidth, this._render());
13
+ });
14
+ }
15
+ disconnectedCallback() {
16
+ var t;
17
+ (t = this._ro) == null || t.disconnect(), this._rafId && cancelAnimationFrame(this._rafId), this._timerId && clearTimeout(this._timerId), window.removeEventListener("zyna-genre", this._genreHandler);
18
+ }
19
+ attributeChangedCallback() {
20
+ this.isConnected && this._render();
21
+ }
22
+ /**
23
+ * Returns the raw string value of an attribute, or `fallback` if absent.
24
+ * @param {string} name
25
+ * @param {*} fallback
26
+ * @returns {string|*}
27
+ */
28
+ _attr(t, e) {
29
+ const i = this.getAttribute(t);
30
+ return i !== null ? i : e;
31
+ }
32
+ /**
33
+ * Returns the JSON-parsed value of an attribute, or `fallback` if absent or unparseable.
34
+ * @param {string} name
35
+ * @param {*} fallback
36
+ * @returns {*}
37
+ */
38
+ _json(t, e) {
39
+ const i = this.getAttribute(t);
40
+ if (i === null) return e;
41
+ try {
42
+ return JSON.parse(i);
43
+ } catch {
44
+ return e;
45
+ }
46
+ }
47
+ /**
48
+ * A stable, per-instance unique string for use in SVG `id` / `url()` references.
49
+ * Uses `crypto.randomUUID()` when available, falling back to `Math.random()`.
50
+ * @returns {string}
51
+ */
52
+ get _uid() {
53
+ return this.__uid || (this.__uid = typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : Math.random().toString(36).slice(2, 10)), this.__uid;
54
+ }
55
+ /**
56
+ * Logs a dev-friendly warning when a chart is mounted with no data.
57
+ * Silenced in production by a `data-silent` attribute on the element.
58
+ * @param {string} tag Custom element tag name e.g. 'zyna-waffle'
59
+ */
60
+ _warnEmpty(t) {
61
+ this.hasAttribute("data-silent") || console.warn(`[${t}] No data provided. Add a \`data\` attribute with a JSON array.`);
62
+ }
63
+ /**
64
+ * Formats a numeric value using a D3-style format string.
65
+ * Supports: '$' prefix, ',' thousands separator, '.Nf' decimals, '%' (×100 + suffix).
66
+ * Returns the value unchanged if no format string is provided.
67
+ * @param {number|string} value
68
+ * @param {string} fmt e.g. '$,.0f' | ',.2f' | '.1%' | ''
69
+ * @returns {string}
70
+ */
71
+ _fmt(t, e) {
72
+ if (!e) return t;
73
+ const i = Number(t);
74
+ if (isNaN(i)) return String(t);
75
+ const r = e.includes("%"), d = e.includes("$"), o = e.includes(","), s = e.match(/\.(\d+)/), n = s ? parseInt(s[1]) : r ? 1 : 0, a = r ? i * 100 : i, c = o ? a.toLocaleString("en-US", { minimumFractionDigits: n, maximumFractionDigits: n }) : a.toFixed(n);
76
+ return (d ? "$" : "") + c + (r ? "%" : "");
77
+ }
78
+ _brand() {
79
+ return getComputedStyle(document.documentElement).getPropertyValue("--zyna").trim() || "#C9A84C";
80
+ }
81
+ _brandDark() {
82
+ return getComputedStyle(document.documentElement).getPropertyValue("--zyna-dark").trim() || "#7A6230";
83
+ }
84
+ _render() {
85
+ }
86
+ }
87
+ export {
88
+ l as Z
89
+ };
@@ -0,0 +1 @@
1
+ "use strict";class h extends HTMLElement{connectedCallback(){this._lastW=0,this._rafId=null,this._timerId=null,this._genreHandler=()=>this._render(),window.addEventListener("zyna-genre",this._genreHandler),this._ro=new ResizeObserver(t=>{var r;const e=((r=t[0])==null?void 0:r.contentRect.width)??this.clientWidth;if(Math.abs(e-this._lastW)<3)return;const i=this._lastW===0;this._lastW=e,this._rafId&&cancelAnimationFrame(this._rafId),this._rafId=requestAnimationFrame(()=>{this._rafId=null,clearTimeout(this._timerId),i?this._render():this._timerId=setTimeout(()=>this._render(),150)})}),this._ro.observe(this),requestAnimationFrame(()=>{this._lastW===0&&(this._lastW=this.clientWidth,this._render())})}disconnectedCallback(){var t;(t=this._ro)==null||t.disconnect(),this._rafId&&cancelAnimationFrame(this._rafId),this._timerId&&clearTimeout(this._timerId),window.removeEventListener("zyna-genre",this._genreHandler)}attributeChangedCallback(){this.isConnected&&this._render()}_attr(t,e){const i=this.getAttribute(t);return i!==null?i:e}_json(t,e){const i=this.getAttribute(t);if(i===null)return e;try{return JSON.parse(i)}catch{return e}}get _uid(){return this.__uid||(this.__uid=typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID().slice(0,8):Math.random().toString(36).slice(2,10)),this.__uid}_warnEmpty(t){this.hasAttribute("data-silent")||console.warn(`[${t}] No data provided. Add a \`data\` attribute with a JSON array.`)}_fmt(t,e){if(!e)return t;const i=Number(t);if(isNaN(i))return String(t);const r=e.includes("%"),d=e.includes("$"),o=e.includes(","),s=e.match(/\.(\d+)/),n=s?parseInt(s[1]):r?1:0,a=r?i*100:i,c=o?a.toLocaleString("en-US",{minimumFractionDigits:n,maximumFractionDigits:n}):a.toFixed(n);return(d?"$":"")+c+(r?"%":"")}_brand(){return getComputedStyle(document.documentElement).getPropertyValue("--zyna").trim()||"#C9A84C"}_brandDark(){return getComputedStyle(document.documentElement).getPropertyValue("--zyna-dark").trim()||"#7A6230"}_render(){}}exports.ZynaChart=h;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const H=require("../base-DyLOr2iL.cjs"),d=require("d3-selection"),T=require("d3-array"),W=require("d3-scale");class y extends H.ZynaChart{static get observedAttributes(){return["data","color","theme","highlight","muted-color","height","show-values","label-format","ticks"]}_render(){var g;const o=this._json("data",[]),b=this._attr("color",this._brand()),f=this._attr("muted-color",""),k=f||this._brandDark(),_=f||"#8A8478",v=this._attr("highlight","")||((g=o[0])==null?void 0:g.label)||"",w=this._attr("label-format",""),A=t=>this._fmt(t,w),M=this._attr("show-values","true")!=="false",u=parseInt(this._attr("height","0")),E=parseInt(this._attr("ticks","5")),z=this._attr("theme","dark")!=="light"?"#F0EBE0":"#1A1A20";if(!o.length){this._warnEmpty("zyna-lollipop");return}const S=(T.max(o,t=>t.value)||1)*1.1,l=this.clientWidth||420,$=Math.max(44,Math.min(70,l*.14)),r=u>0?u:Math.max(200,o.length*$+60),e={left:Math.max(60,l*.18),right:Math.max(50,l*.15),top:10,bottom:28},j=l-e.left-e.right,q=r-e.top-e.bottom,m=W.scaleLinear().domain([0,S]).range([0,j]),i=Math.max(9,l*.022),L=Math.max(11,l*.026);let n=d.select(this).select("svg");n.empty()&&(n=d.select(this).append("svg").style("display","block")),n.attr("viewBox",`0 0 ${l} ${r}`).attr("width",l).attr("height",r);const V=m.ticks(E);n.selectAll("g.ll-tick").data(V,t=>t).join(t=>{const a=t.append("g").attr("class","ll-tick");return a.append("line").attr("class","ll-tick-line"),a.append("text").attr("class","ll-tick-label"),a}).each(function(t){const a=d.select(this),s=e.left+m(t);a.select(".ll-tick-line").attr("x1",s).attr("x2",s).attr("y1",e.top).attr("y2",r-e.bottom+4).attr("stroke","#1E1E24").attr("stroke-width",.8),a.select(".ll-tick-label").attr("x",s).attr("y",r-e.bottom+16).attr("text-anchor","middle").attr("font-family","monospace").attr("font-size",`${i}px`).attr("fill","#5A5050").text(t)}),n.selectAll("g.ll-row").data(o,t=>t.label).join(t=>{const a=t.append("g").attr("class","ll-row");return a.append("line").attr("class","ll-stem"),a.append("circle").attr("class","ll-dot"),a.append("text").attr("class","ll-label"),a.append("text").attr("class","ll-value"),a}).each(function(t,a){const s=d.select(this),c=e.top+(a+.5)*(q/o.length),p=e.left+m(t.value),h=t.label===v,x=h?b:k,C=h?z:_;s.select(".ll-stem").attr("x1",e.left).attr("x2",p).attr("y1",c).attr("y2",c).attr("stroke",x).attr("stroke-width",h?1.5:1),s.select(".ll-dot").attr("cx",p).attr("cy",c).attr("r",h?7:5).attr("fill",x),s.select(".ll-label").attr("x",e.left-6).attr("y",c+i*.4).attr("text-anchor","end").attr("font-size",`${L}px`).attr("fill",C).text(t.label),s.select(".ll-value").attr("display",M?null:"none").attr("x",p+10).attr("y",c+i*.4).attr("font-family","monospace").attr("font-size",`${i}px`).attr("fill",x).text(A(t.value))})}}customElements.get("zyna-lollipop")||customElements.define("zyna-lollipop",y);exports.ZynaLollipop=y;
@@ -0,0 +1,38 @@
1
+ import { Z as W } from "../base-CaP9Us8w.js";
2
+ import { select as m } from "d3-selection";
3
+ import { max as Z } from "d3-array";
4
+ import { scaleLinear as B } from "d3-scale";
5
+ class I extends W {
6
+ static get observedAttributes() {
7
+ return ["data", "color", "theme", "highlight", "muted-color", "height", "show-values", "label-format", "ticks"];
8
+ }
9
+ _render() {
10
+ var u;
11
+ const o = this._json("data", []), y = this._attr("color", this._brand()), f = this._attr("muted-color", ""), b = f || this._brandDark(), k = f || "#8A8478", _ = this._attr("highlight", "") || ((u = o[0]) == null ? void 0 : u.label) || "", v = this._attr("label-format", ""), w = (t) => this._fmt(t, v), A = this._attr("show-values", "true") !== "false", g = parseInt(this._attr("height", "0")), M = parseInt(this._attr("ticks", "5")), E = this._attr("theme", "dark") !== "light" ? "#F0EBE0" : "#1A1A20";
12
+ if (!o.length) {
13
+ this._warnEmpty("zyna-lollipop");
14
+ return;
15
+ }
16
+ const z = (Z(o, (t) => t.value) || 1) * 1.1, e = this.clientWidth || 420, $ = Math.max(44, Math.min(70, e * 0.14)), r = g > 0 ? g : Math.max(200, o.length * $ + 60), l = { left: Math.max(60, e * 0.18), right: Math.max(50, e * 0.15), top: 10, bottom: 28 }, V = e - l.left - l.right, j = r - l.top - l.bottom, p = B().domain([0, z]).range([0, V]), i = Math.max(9, e * 0.022), C = Math.max(11, e * 0.026);
17
+ let n = m(this).select("svg");
18
+ n.empty() && (n = m(this).append("svg").style("display", "block")), n.attr("viewBox", `0 0 ${e} ${r}`).attr("width", e).attr("height", r);
19
+ const H = p.ticks(M);
20
+ n.selectAll("g.ll-tick").data(H, (t) => t).join((t) => {
21
+ const a = t.append("g").attr("class", "ll-tick");
22
+ return a.append("line").attr("class", "ll-tick-line"), a.append("text").attr("class", "ll-tick-label"), a;
23
+ }).each(function(t) {
24
+ const a = m(this), s = l.left + p(t);
25
+ a.select(".ll-tick-line").attr("x1", s).attr("x2", s).attr("y1", l.top).attr("y2", r - l.bottom + 4).attr("stroke", "#1E1E24").attr("stroke-width", 0.8), a.select(".ll-tick-label").attr("x", s).attr("y", r - l.bottom + 16).attr("text-anchor", "middle").attr("font-family", "monospace").attr("font-size", `${i}px`).attr("fill", "#5A5050").text(t);
26
+ }), n.selectAll("g.ll-row").data(o, (t) => t.label).join((t) => {
27
+ const a = t.append("g").attr("class", "ll-row");
28
+ return a.append("line").attr("class", "ll-stem"), a.append("circle").attr("class", "ll-dot"), a.append("text").attr("class", "ll-label"), a.append("text").attr("class", "ll-value"), a;
29
+ }).each(function(t, a) {
30
+ const s = m(this), c = l.top + (a + 0.5) * (j / o.length), d = l.left + p(t.value), h = t.label === _, x = h ? y : b, L = h ? E : k;
31
+ s.select(".ll-stem").attr("x1", l.left).attr("x2", d).attr("y1", c).attr("y2", c).attr("stroke", x).attr("stroke-width", h ? 1.5 : 1), s.select(".ll-dot").attr("cx", d).attr("cy", c).attr("r", h ? 7 : 5).attr("fill", x), s.select(".ll-label").attr("x", l.left - 6).attr("y", c + i * 0.4).attr("text-anchor", "end").attr("font-size", `${C}px`).attr("fill", L).text(t.label), s.select(".ll-value").attr("display", A ? null : "none").attr("x", d + 10).attr("y", c + i * 0.4).attr("font-family", "monospace").attr("font-size", `${i}px`).attr("fill", x).text(w(t.value));
32
+ });
33
+ }
34
+ }
35
+ customElements.get("zyna-lollipop") || customElements.define("zyna-lollipop", I);
36
+ export {
37
+ I as ZynaLollipop
38
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("../base-DyLOr2iL.cjs"),b=require("d3-selection"),O=require("d3-array"),W=require("d3-shape");class C extends N.ZynaChart{static get observedAttributes(){return["data","color","theme","height","show-values","label-format"]}_render(){const i=this._json("data",[]),R=this._attr("color",this._brand()),p=this._attr("theme","dark")!=="light",S=this._attr("label-format",""),j=t=>this._fmt(t,S),B=this._attr("show-values","true")!=="false",v=parseInt(this._attr("height","0")),I=p?"#F0EBE0":"#1A1A20",$=p?"#0C0C0F":"#FFFFFF";if(!i.length){this._warnEmpty("zyna-nightingale");return}const e=this.clientWidth||700,f=v>0?v:Math.max(400,e*.8);let s=b.select(this).select("svg");s.empty()&&(s=b.select(this).append("svg").style("display","block")),s.attr("viewBox",`0 0 ${e} ${f}`).attr("width",e).attr("height",f);const n=e/2,r=f/2,x=Math.min(n,r)*.52,P=O.max(i,t=>t.value)||1,V=i.length,A=2*Math.PI/V,_=x+Math.max(70,e*.14),k=Math.max(6,x*.06),h=Math.max(10,e*.015),Z=Math.max(12,e*.018);s.selectAll("g.ng-sector").data(i,t=>t.label).join(t=>{const a=t.append("g").attr("class","ng-sector");return a.append("path").attr("class","ng-arc"),a.append("text").attr("class","ng-label"),a.append("text").attr("class","ng-value"),a.append("polyline").attr("class","ng-leader"),a.append("circle").attr("class","ng-dot"),a}).each(function(t,a){const l=b.select(this),y=a*A-Math.PI/2,w=y+A,M=x*Math.sqrt(t.value/P),c=(y+w)/2,g=t.color||R;l.select(".ng-arc").attr("d",W.arc().innerRadius(k).outerRadius(M).startAngle(y).endAngle(w)).attr("transform",`translate(${n},${r})`).attr("fill",g).attr("stroke",$).attr("stroke-width",1.5);const E=n+(M+10)*Math.sin(c),F=r-(M+10)*Math.cos(c),d=n+_*Math.sin(c),m=r-_*Math.cos(c),o=Math.sin(c),q=o<-.1?"end":o>.1?"start":"middle",z=o>.1?6:o<-.1?-6:0;l.select(".ng-label").attr("x",d+z).attr("y",m-4).attr("text-anchor",q).attr("font-size",`${Z}px`).attr("fill",I).text(t.label),l.select(".ng-value").attr("display",B?null:"none").attr("x",d+z).attr("y",m+h+2).attr("text-anchor",q).attr("font-family","monospace").attr("font-size",`${h}px`).attr("fill",g).text(j(t.value)),l.select(".ng-leader").attr("points",`${d},${m+h+12} ${d-o*24},${m+h+12} ${E},${F}`).attr("fill","none").attr("stroke",g).attr("stroke-width",.8).attr("opacity",.5),l.select(".ng-dot").attr("cx",E).attr("cy",F).attr("r",3).attr("fill",g)});let u=s.select(".ng-cap");u.empty()&&(u=s.append("circle").attr("class","ng-cap")),u.attr("cx",n).attr("cy",r).attr("r",k*1.4).attr("fill",$).attr("stroke",p?"#2A2A30":"#E5E7EB").attr("stroke-width",1)}}customElements.get("zyna-nightingale")||customElements.define("zyna-nightingale",C);exports.ZynaNightingale=C;