@tolle_/tolle-ui 18.3.0 → 18.3.2

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 (37) hide show
  1. package/esm2022/lib/alert-dialog-dynamic.component.mjs +13 -9
  2. package/esm2022/lib/alert-dialog.types.mjs +6 -1
  3. package/esm2022/lib/bar-list.component.mjs +95 -0
  4. package/esm2022/lib/category-bar.component.mjs +130 -0
  5. package/esm2022/lib/chart-spark.component.mjs +120 -0
  6. package/esm2022/lib/chart.component.mjs +131 -45
  7. package/esm2022/lib/chart.service.mjs +33 -8
  8. package/esm2022/lib/progress-circle.component.mjs +138 -0
  9. package/esm2022/lib/tracker.component.mjs +84 -0
  10. package/esm2022/public-api.mjs +6 -1
  11. package/fesm2022/tolle-ui.mjs +722 -60
  12. package/fesm2022/tolle-ui.mjs.map +1 -1
  13. package/lib/alert-dialog-dynamic.component.d.ts +7 -1
  14. package/lib/alert-dialog.types.d.ts +2 -0
  15. package/lib/bar-list.component.d.ts +39 -0
  16. package/lib/category-bar.component.d.ts +49 -0
  17. package/lib/chart-spark.component.d.ts +49 -0
  18. package/lib/chart.component.d.ts +68 -9
  19. package/lib/chart.service.d.ts +25 -5
  20. package/lib/progress-circle.component.d.ts +35 -0
  21. package/lib/tracker.component.d.ts +39 -0
  22. package/package.json +1 -1
  23. package/public-api.d.ts +5 -0
  24. package/registry/docs-content.json +330 -2
  25. package/registry/llms-full.txt +98 -2
  26. package/registry/llms.txt +5 -0
  27. package/registry/manifest.json +380 -3
  28. package/registry/r/alert-dialog-dynamic.json +2 -2
  29. package/registry/r/alert-dialog.json +1 -1
  30. package/registry/r/bar-list.json +21 -0
  31. package/registry/r/category-bar.json +21 -0
  32. package/registry/r/chart-pie.json +1 -1
  33. package/registry/r/chart-spark.json +30 -0
  34. package/registry/r/chart.json +2 -2
  35. package/registry/r/progress-circle.json +21 -0
  36. package/registry/r/tracker.json +25 -0
  37. package/registry/registry.json +112 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@tolle_/tolle-ui",
3
- "version": "18.3.0",
3
+ "version": "18.3.2",
4
4
  "registry": "r/{name}.json",
5
5
  "components": [
6
6
  {
@@ -860,6 +860,62 @@
860
860
  }
861
861
  ]
862
862
  },
863
+ {
864
+ "name": "bar-list",
865
+ "title": "Bar List",
866
+ "description": "Bar List component.",
867
+ "category": "components",
868
+ "selector": "tolle-bar-list",
869
+ "isNew": true,
870
+ "import": "import { BarListComponent } from '@tolle_/tolle-ui';",
871
+ "install": "npx @tolle_/cli add bar-list",
872
+ "registryDependencies": [],
873
+ "dependencies": [],
874
+ "files": [
875
+ "bar-list.component.ts",
876
+ "utils/cn.ts"
877
+ ],
878
+ "components": [
879
+ {
880
+ "className": "BarListComponent",
881
+ "selector": "tolle-bar-list",
882
+ "kind": "component",
883
+ "inputs": [
884
+ {
885
+ "name": "data",
886
+ "type": "Record<string, any>[]",
887
+ "default": "[]",
888
+ "description": "Rows to rank, in the order they should be drawn.",
889
+ "required": false
890
+ },
891
+ {
892
+ "name": "labelKey",
893
+ "type": "string",
894
+ "default": "'label'",
895
+ "description": "Row key holding each row's label.",
896
+ "required": false
897
+ },
898
+ {
899
+ "name": "valueKey",
900
+ "type": "string",
901
+ "default": "'value'",
902
+ "description": "Row key holding each row's numeric value.",
903
+ "required": false
904
+ },
905
+ {
906
+ "name": "class",
907
+ "type": "string",
908
+ "default": "''",
909
+ "description": "Extra Tailwind classes merged onto the list via `cn()` (last-wins).",
910
+ "required": false
911
+ }
912
+ ],
913
+ "outputs": [],
914
+ "slots": [],
915
+ "variants": []
916
+ }
917
+ ]
918
+ },
863
919
  {
864
920
  "name": "breadcrumb",
865
921
  "title": "Breadcrumb",
@@ -1671,6 +1727,76 @@
1671
1727
  }
1672
1728
  ]
1673
1729
  },
1730
+ {
1731
+ "name": "category-bar",
1732
+ "title": "Category Bar",
1733
+ "description": "Category Bar component.",
1734
+ "category": "components",
1735
+ "selector": "tolle-category-bar",
1736
+ "isNew": true,
1737
+ "import": "import { CategoryBarComponent } from '@tolle_/tolle-ui';",
1738
+ "install": "npx @tolle_/cli add category-bar",
1739
+ "registryDependencies": [],
1740
+ "dependencies": [],
1741
+ "files": [
1742
+ "category-bar.component.ts",
1743
+ "utils/cn.ts"
1744
+ ],
1745
+ "components": [
1746
+ {
1747
+ "className": "CategoryBarComponent",
1748
+ "selector": "tolle-category-bar",
1749
+ "kind": "component",
1750
+ "inputs": [
1751
+ {
1752
+ "name": "values",
1753
+ "type": "number[]",
1754
+ "default": "[]",
1755
+ "description": "Width of each segment. Values that are not finite and greater than zero are dropped.",
1756
+ "required": false
1757
+ },
1758
+ {
1759
+ "name": "labels",
1760
+ "type": "string[]",
1761
+ "default": "[]",
1762
+ "description": "Name for each segment, used in the accessible summary and as a title tooltip.",
1763
+ "required": false
1764
+ },
1765
+ {
1766
+ "name": "colors",
1767
+ "type": "string[]",
1768
+ "default": "[]",
1769
+ "description": "Paint for each segment, by index. Falls back to the chart palette --chart-1 through --chart-5.",
1770
+ "required": false
1771
+ },
1772
+ {
1773
+ "name": "markerValue",
1774
+ "type": "number | null",
1775
+ "default": "null",
1776
+ "description": "Position of the pointer, 0-100 on the same scale the segments sum to. Omit to draw no pointer.",
1777
+ "required": false
1778
+ },
1779
+ {
1780
+ "name": "ariaLabel",
1781
+ "type": "string",
1782
+ "default": "''",
1783
+ "description": "Accessible name, prefixed onto the sr-only summary of segments and marker.",
1784
+ "required": false
1785
+ },
1786
+ {
1787
+ "name": "class",
1788
+ "type": "string",
1789
+ "default": "''",
1790
+ "description": "Extra Tailwind classes merged onto the bar via `cn()` (last-wins).",
1791
+ "required": false
1792
+ }
1793
+ ],
1794
+ "outputs": [],
1795
+ "slots": [],
1796
+ "variants": []
1797
+ }
1798
+ ]
1799
+ },
1674
1800
  {
1675
1801
  "name": "chain-of-thought",
1676
1802
  "title": "Chain Of Thought",
@@ -2094,6 +2220,13 @@
2094
2220
  "description": "Stacks marks on a shared baseline instead of grouping them.",
2095
2221
  "required": false
2096
2222
  },
2223
+ {
2224
+ "name": "orientation",
2225
+ "type": "ChartOrientation",
2226
+ "default": "'vertical'",
2227
+ "description": "Which physical axis carries the category vs the value. Only bars support\n'horizontal' — line/area marks stay vertical regardless.",
2228
+ "required": false
2229
+ },
2097
2230
  {
2098
2231
  "name": "hover",
2099
2232
  "type": "boolean",
@@ -2177,7 +2310,7 @@
2177
2310
  "name": "vertical",
2178
2311
  "type": "boolean",
2179
2312
  "default": "false",
2180
- "description": "Also draws a rule at every x position.",
2313
+ "description": "Also draws a rule at every category position.",
2181
2314
  "required": false
2182
2315
  },
2183
2316
  {
@@ -2201,7 +2334,7 @@
2201
2334
  "name": "charWidth",
2202
2335
  "type": "number",
2203
2336
  "default": "7",
2204
- "description": "Approximate px per character, used to decide when labels would collide.",
2337
+ "description": "Approximate px per character, used to decide when category labels would\ncollide. Only consulted in the vertical orientation.",
2205
2338
  "required": false
2206
2339
  },
2207
2340
  {
@@ -2516,6 +2649,130 @@
2516
2649
  }
2517
2650
  ]
2518
2651
  },
2652
+ {
2653
+ "name": "chart-spark",
2654
+ "title": "Chart Spark",
2655
+ "description": "Chart Spark component.",
2656
+ "category": "data",
2657
+ "selector": "tolle-chart-spark",
2658
+ "isNew": true,
2659
+ "import": "import { ChartSparkComponent } from '@tolle_/tolle-ui';",
2660
+ "install": "npx @tolle_/cli add chart-spark",
2661
+ "registryDependencies": [
2662
+ "chart"
2663
+ ],
2664
+ "dependencies": [
2665
+ "class-variance-authority"
2666
+ ],
2667
+ "files": [
2668
+ "chart-spark.component.ts",
2669
+ "chart.service.ts",
2670
+ "utils/cn.ts"
2671
+ ],
2672
+ "components": [
2673
+ {
2674
+ "className": "ChartSparkComponent",
2675
+ "selector": "tolle-chart-spark",
2676
+ "kind": "component",
2677
+ "inputs": [
2678
+ {
2679
+ "name": "data",
2680
+ "type": "Record<string, any>[]",
2681
+ "default": "[]",
2682
+ "description": "Rows to plot, in x order.",
2683
+ "required": false
2684
+ },
2685
+ {
2686
+ "name": "valueKey",
2687
+ "type": "string",
2688
+ "default": "'value'",
2689
+ "description": "Row key holding each row's numeric value.",
2690
+ "required": false
2691
+ },
2692
+ {
2693
+ "name": "xKey",
2694
+ "type": "string",
2695
+ "default": "''",
2696
+ "description": "Row key holding each row's x position. Only matters once `hover` is on.",
2697
+ "required": false
2698
+ },
2699
+ {
2700
+ "name": "type",
2701
+ "type": "'line' | 'area' | 'bar'",
2702
+ "default": "'line'",
2703
+ "description": "Which mark to draw.",
2704
+ "required": false
2705
+ },
2706
+ {
2707
+ "name": "curve",
2708
+ "type": "'linear' | 'smooth'",
2709
+ "default": "'linear'",
2710
+ "description": "Interpolation for line/area.",
2711
+ "required": false
2712
+ },
2713
+ {
2714
+ "name": "maxBarWidth",
2715
+ "type": "number",
2716
+ "default": "6",
2717
+ "description": "Largest bar thickness in px, used when `type` is 'bar'.",
2718
+ "required": false
2719
+ },
2720
+ {
2721
+ "name": "height",
2722
+ "type": "number",
2723
+ "default": "32",
2724
+ "description": "Height in px.",
2725
+ "required": false
2726
+ },
2727
+ {
2728
+ "name": "hover",
2729
+ "type": "boolean",
2730
+ "default": "false",
2731
+ "description": "Renders the crosshair, hit layer and tooltip.",
2732
+ "required": false
2733
+ },
2734
+ {
2735
+ "name": "showTable",
2736
+ "type": "boolean",
2737
+ "default": "false",
2738
+ "description": "Shows the data table instead of leaving it visually hidden.",
2739
+ "required": false
2740
+ },
2741
+ {
2742
+ "name": "ariaLabel",
2743
+ "type": "string",
2744
+ "default": "''",
2745
+ "description": "Accessible name, used as the svg <title>.",
2746
+ "required": false
2747
+ },
2748
+ {
2749
+ "name": "description",
2750
+ "type": "string",
2751
+ "default": "''",
2752
+ "description": "Longer summary used as the svg <desc>. Falls back to `ariaLabel`.",
2753
+ "required": false
2754
+ },
2755
+ {
2756
+ "name": "margin",
2757
+ "type": "ChartMargin",
2758
+ "default": "{ top: 2, right: 2, bottom: 2, left: 2 }",
2759
+ "description": "Space reserved around the plot — a sparkline has no axis labels to fit.",
2760
+ "required": false
2761
+ },
2762
+ {
2763
+ "name": "class",
2764
+ "type": "string",
2765
+ "default": "''",
2766
+ "description": "Extra Tailwind classes merged onto the chart via `cn()` (last-wins).",
2767
+ "required": false
2768
+ }
2769
+ ],
2770
+ "outputs": [],
2771
+ "slots": [],
2772
+ "variants": []
2773
+ }
2774
+ ]
2775
+ },
2519
2776
  {
2520
2777
  "name": "checkbox",
2521
2778
  "title": "Checkbox",
@@ -8501,6 +8758,66 @@
8501
8758
  }
8502
8759
  ]
8503
8760
  },
8761
+ {
8762
+ "name": "progress-circle",
8763
+ "title": "Progress Circle",
8764
+ "description": "Progress Circle component.",
8765
+ "category": "feedback",
8766
+ "selector": "tolle-progress-circle",
8767
+ "isNew": true,
8768
+ "import": "import { ProgressCircleComponent } from '@tolle_/tolle-ui';",
8769
+ "install": "npx @tolle_/cli add progress-circle",
8770
+ "registryDependencies": [],
8771
+ "dependencies": [],
8772
+ "files": [
8773
+ "progress-circle.component.ts",
8774
+ "utils/cn.ts"
8775
+ ],
8776
+ "components": [
8777
+ {
8778
+ "className": "ProgressCircleComponent",
8779
+ "selector": "tolle-progress-circle",
8780
+ "kind": "component",
8781
+ "inputs": [
8782
+ {
8783
+ "name": "value",
8784
+ "type": "number | null",
8785
+ "default": "0",
8786
+ "description": "Percent complete, 0-100.",
8787
+ "required": false
8788
+ },
8789
+ {
8790
+ "name": "size",
8791
+ "type": "number",
8792
+ "default": "120",
8793
+ "description": "Diameter of the ring in px.",
8794
+ "required": false
8795
+ },
8796
+ {
8797
+ "name": "strokeWidth",
8798
+ "type": "number",
8799
+ "default": "8",
8800
+ "description": "Thickness of the ring in px.",
8801
+ "required": false
8802
+ },
8803
+ {
8804
+ "name": "class",
8805
+ "type": "string",
8806
+ "default": "''",
8807
+ "description": "Extra Tailwind classes merged onto the ring via `cn()` (last-wins).",
8808
+ "required": false
8809
+ }
8810
+ ],
8811
+ "outputs": [],
8812
+ "slots": [
8813
+ {
8814
+ "name": ""
8815
+ }
8816
+ ],
8817
+ "variants": []
8818
+ }
8819
+ ]
8820
+ },
8504
8821
  {
8505
8822
  "name": "prompt-input",
8506
8823
  "title": "Prompt Input",
@@ -12209,6 +12526,66 @@
12209
12526
  }
12210
12527
  ]
12211
12528
  },
12529
+ {
12530
+ "name": "tracker",
12531
+ "title": "Tracker",
12532
+ "description": "Tracker component.",
12533
+ "category": "components",
12534
+ "selector": "tolle-tracker",
12535
+ "isNew": true,
12536
+ "import": "import { TrackerComponent } from '@tolle_/tolle-ui';",
12537
+ "install": "npx @tolle_/cli add tracker",
12538
+ "registryDependencies": [
12539
+ "tooltip"
12540
+ ],
12541
+ "dependencies": [
12542
+ "@floating-ui/dom"
12543
+ ],
12544
+ "files": [
12545
+ "tracker.component.ts",
12546
+ "utils/cn.ts"
12547
+ ],
12548
+ "components": [
12549
+ {
12550
+ "className": "TrackerComponent",
12551
+ "selector": "tolle-tracker",
12552
+ "kind": "component",
12553
+ "inputs": [
12554
+ {
12555
+ "name": "data",
12556
+ "type": "TrackerBlock[]",
12557
+ "default": "[]",
12558
+ "description": "One block per period, in chronological order.",
12559
+ "required": false
12560
+ },
12561
+ {
12562
+ "name": "blockHeight",
12563
+ "type": "number",
12564
+ "default": "32",
12565
+ "description": "Height of each block in px.",
12566
+ "required": false
12567
+ },
12568
+ {
12569
+ "name": "ariaLabel",
12570
+ "type": "string",
12571
+ "default": "''",
12572
+ "description": "Accessible name for the whole strip.",
12573
+ "required": false
12574
+ },
12575
+ {
12576
+ "name": "class",
12577
+ "type": "string",
12578
+ "default": "''",
12579
+ "description": "Extra Tailwind classes merged onto the strip via `cn()` (last-wins).",
12580
+ "required": false
12581
+ }
12582
+ ],
12583
+ "outputs": [],
12584
+ "slots": [],
12585
+ "variants": []
12586
+ }
12587
+ ]
12588
+ },
12212
12589
  {
12213
12590
  "name": "typography",
12214
12591
  "title": "Typography",
@@ -15,12 +15,12 @@
15
15
  {
16
16
  "path": "alert-dialog-dynamic.component.ts",
17
17
  "target": "ui/alert-dialog-dynamic.component.ts",
18
- "content": "import { Component, Inject, inject, OnInit } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n AlertDialogComponent,\n AlertDialogContentComponent,\n AlertDialogHeaderComponent,\n AlertDialogTitleComponent,\n AlertDialogDescriptionComponent,\n AlertDialogFooterComponent,\n AlertDialogCancelComponent,\n AlertDialogActionComponent\n} from './alert-dialog.component';\nimport { ButtonComponent } from './button.component';\nimport { AlertDialogConfig, AlertDialogRef } from './alert-dialog.types';\n\n@Component({\n selector: 'tolle-alert-dialog-dynamic',\n styles: [':host { display: block; }'],\n standalone: true,\n imports: [\n CommonModule,\n AlertDialogComponent,\n AlertDialogContentComponent,\n AlertDialogHeaderComponent,\n AlertDialogTitleComponent,\n AlertDialogDescriptionComponent,\n AlertDialogFooterComponent,\n AlertDialogCancelComponent,\n AlertDialogActionComponent,\n ButtonComponent\n ],\n template: `\n <tolle-alert-dialog [open]=\"!closing\" (openChange)=\"onOpenChange($event)\">\n <tolle-alert-dialog-content [size]=\"config.size || 'lg'\">\n <tolle-alert-dialog-header>\n <tolle-alert-dialog-title>{{ config.title }}</tolle-alert-dialog-title>\n <tolle-alert-dialog-description>\n {{ config.description }}\n </tolle-alert-dialog-description>\n </tolle-alert-dialog-header>\n <tolle-alert-dialog-footer>\n <tolle-alert-dialog-cancel (click)=\"close(false)\">\n <tolle-button variant=\"outline\">{{ config.cancelText || 'Cancel' }}</tolle-button>\n </tolle-alert-dialog-cancel>\n <tolle-alert-dialog-action (click)=\"close(true)\">\n <tolle-button [variant]=\"config.variant === 'destructive' ? 'destructive' : 'default'\">\n {{ config.actionText || 'Continue' }}\n </tolle-button>\n </tolle-alert-dialog-action>\n </tolle-alert-dialog-footer>\n </tolle-alert-dialog-content>\n </tolle-alert-dialog>\n `\n})\nexport class AlertDialogDynamicComponent {\n config!: AlertDialogConfig;\n dialogRef!: AlertDialogRef;\n\n /**\n * Set by `AlertDialogService` once the dialog's result is ready, so the\n * content panel plays its exit animation before the overlay is torn down.\n */\n closing = false;\n\n onOpenChange(open: boolean) {\n if (!open) {\n this.close(false);\n }\n }\n\n close(result: boolean) {\n this.dialogRef.close(result);\n }\n}\n"
18
+ "content": "import { Component, Inject, inject, OnInit } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n AlertDialogComponent,\n AlertDialogContentComponent,\n AlertDialogHeaderComponent,\n AlertDialogTitleComponent,\n AlertDialogDescriptionComponent,\n AlertDialogFooterComponent,\n AlertDialogCancelComponent,\n AlertDialogActionComponent\n} from './alert-dialog.component';\nimport { ButtonComponent } from './button.component';\nimport { AlertDialogConfig, AlertDialogRef } from './alert-dialog.types';\n\n@Component({\n selector: 'tolle-alert-dialog-dynamic',\n styles: [':host { display: block; }'],\n standalone: true,\n imports: [\n CommonModule,\n AlertDialogComponent,\n AlertDialogContentComponent,\n AlertDialogHeaderComponent,\n AlertDialogTitleComponent,\n AlertDialogDescriptionComponent,\n AlertDialogFooterComponent,\n AlertDialogCancelComponent,\n AlertDialogActionComponent,\n ButtonComponent\n ],\n template: `\n <tolle-alert-dialog [open]=\"!closing\" (openChange)=\"onOpenChange($event)\">\n <tolle-alert-dialog-content [size]=\"config.size || 'lg'\">\n <tolle-alert-dialog-header>\n <tolle-alert-dialog-title>{{ config.title }}</tolle-alert-dialog-title>\n <tolle-alert-dialog-description>\n {{ config.description }}\n </tolle-alert-dialog-description>\n </tolle-alert-dialog-header>\n <tolle-alert-dialog-footer>\n <tolle-alert-dialog-cancel (cancelled)=\"result = false\">\n <tolle-button variant=\"outline\">{{ config.cancelText || 'Cancel' }}</tolle-button>\n </tolle-alert-dialog-cancel>\n <tolle-alert-dialog-action (confirmed)=\"result = true\">\n <tolle-button [variant]=\"config.variant === 'destructive' ? 'destructive' : 'default'\">\n {{ config.actionText || 'Continue' }}\n </tolle-button>\n </tolle-alert-dialog-action>\n </tolle-alert-dialog-footer>\n </tolle-alert-dialog-content>\n </tolle-alert-dialog>\n `\n})\nexport class AlertDialogDynamicComponent {\n config!: AlertDialogConfig;\n dialogRef!: AlertDialogRef;\n\n /**\n * Set by `AlertDialogService` once the dialog's result is ready, so the\n * content panel plays its exit animation before the overlay is torn down.\n */\n closing = false;\n\n /**\n * Result recorded by the action/cancel buttons' `confirmed`/`cancelled`\n * outputs, which fire *before* the button drives the dialog closed. The\n * single close path (`onOpenChange`) then reads it, so a confirm reports\n * `true` instead of racing with the button's own `false` close signal.\n */\n result = false;\n\n onOpenChange(open: boolean) {\n if (!open) {\n this.dialogRef.close(this.result);\n }\n }\n}\n"
19
19
  },
20
20
  {
21
21
  "path": "alert-dialog.types.ts",
22
22
  "target": "ui/alert-dialog.types.ts",
23
- "content": "import { Subject, Observable } from 'rxjs';\n\nexport type AlertDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'fit';\n\nexport interface AlertDialogConfig {\n title: string;\n description: string;\n cancelText?: string;\n actionText?: string;\n variant?: 'default' | 'destructive';\n size?: AlertDialogSize;\n}\n\nexport class AlertDialogRef {\n private readonly _afterClosed = new Subject<boolean>();\n afterClosed$: Observable<boolean> = this._afterClosed.asObservable();\n\n close(result: boolean = false): void {\n this._afterClosed.next(result);\n this._afterClosed.complete();\n }\n}\n"
23
+ "content": "import { Subject, Observable } from 'rxjs';\n\nexport type AlertDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'fit';\n\nexport interface AlertDialogConfig {\n title: string;\n description: string;\n cancelText?: string;\n actionText?: string;\n variant?: 'default' | 'destructive';\n size?: AlertDialogSize;\n}\n\nexport class AlertDialogRef {\n private readonly _afterClosed = new Subject<boolean>();\n afterClosed$: Observable<boolean> = this._afterClosed.asObservable();\n\n /** Guards against the dialog's several close paths racing to emit twice. */\n private closed = false;\n\n close(result: boolean = false): void {\n if (this.closed) return;\n this.closed = true;\n this._afterClosed.next(result);\n this._afterClosed.complete();\n }\n}\n"
24
24
  },
25
25
  {
26
26
  "path": "utils/cn.ts",
@@ -15,7 +15,7 @@
15
15
  {
16
16
  "path": "alert-dialog.types.ts",
17
17
  "target": "ui/alert-dialog.types.ts",
18
- "content": "import { Subject, Observable } from 'rxjs';\n\nexport type AlertDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'fit';\n\nexport interface AlertDialogConfig {\n title: string;\n description: string;\n cancelText?: string;\n actionText?: string;\n variant?: 'default' | 'destructive';\n size?: AlertDialogSize;\n}\n\nexport class AlertDialogRef {\n private readonly _afterClosed = new Subject<boolean>();\n afterClosed$: Observable<boolean> = this._afterClosed.asObservable();\n\n close(result: boolean = false): void {\n this._afterClosed.next(result);\n this._afterClosed.complete();\n }\n}\n"
18
+ "content": "import { Subject, Observable } from 'rxjs';\n\nexport type AlertDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'fit';\n\nexport interface AlertDialogConfig {\n title: string;\n description: string;\n cancelText?: string;\n actionText?: string;\n variant?: 'default' | 'destructive';\n size?: AlertDialogSize;\n}\n\nexport class AlertDialogRef {\n private readonly _afterClosed = new Subject<boolean>();\n afterClosed$: Observable<boolean> = this._afterClosed.asObservable();\n\n /** Guards against the dialog's several close paths racing to emit twice. */\n private closed = false;\n\n close(result: boolean = false): void {\n if (this.closed) return;\n this.closed = true;\n this._afterClosed.next(result);\n this._afterClosed.complete();\n }\n}\n"
19
19
  },
20
20
  {
21
21
  "path": "utils/cn.ts",
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "bar-list",
3
+ "type": "registry:ui",
4
+ "title": "Bar List",
5
+ "description": "Bar List component.",
6
+ "category": "components",
7
+ "registryDependencies": [],
8
+ "dependencies": [],
9
+ "files": [
10
+ {
11
+ "path": "bar-list.component.ts",
12
+ "target": "ui/bar-list.component.ts",
13
+ "content": "import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, OnChanges, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { cn } from './utils/cn';\n\n/** One resolved row: its label, its value, and its share of the list's largest value. */\nexport interface BarListRow {\n label: string;\n value: number;\n percent: number;\n formattedValue: string;\n}\n\n/**\n * A ranked list of horizontal bars — label left, value right, bar width\n * scaled to each row's share of the list's largest value. Unlike a chart\n * series or a category bar's segments, every bar is the same accent: rank is\n * carried by length, not by a categorical palette.\n * @new\n */\n@Component({\n selector: 'tolle-bar-list',\n styles: [':host { display: block; }'],\n standalone: true,\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div [class]=\"computedClass\">\n <div\n *ngFor=\"let row of rows; trackBy: trackByIndex\"\n class=\"relative flex items-center overflow-hidden rounded-sm\"\n >\n <div class=\"absolute inset-y-0 left-0 rounded-sm bg-primary/15\" [style.width.%]=\"row.percent\"></div>\n <span class=\"relative z-10 truncate px-2 py-1.5 text-sm text-foreground\">{{ row.label }}</span>\n <span class=\"relative z-10 ml-auto shrink-0 px-2 py-1.5 text-sm font-medium tabular-nums text-foreground\">\n {{ row.formattedValue }}\n </span>\n </div>\n </div>\n `,\n})\nexport class BarListComponent implements OnChanges {\n /**\n * Angular writes a bound `class` input through its styling path, which does\n * not mark an OnPush component dirty — without this hook the component keeps\n * rendering the class it was born with.\n */\n ngOnChanges(): void {\n this.cdr.markForCheck();\n }\n\n /** Identity for `*ngFor`, so a redraw patches nodes instead of replacing them. */\n trackByIndex = (index: number): number => index;\n\n /** Rows to rank, in the order they should be drawn. @default [] */\n @Input() data: Record<string, any>[] = [];\n /** Row key holding each row's label. @default 'label' */\n @Input() labelKey = 'label';\n /** Row key holding each row's numeric value. @default 'value' */\n @Input() valueKey = 'value';\n /** Extra Tailwind classes merged onto the list via `cn()` (last-wins). */\n @Input() class = '';\n\n private readonly cdr = inject(ChangeDetectorRef);\n\n get rows(): BarListRow[] {\n const rows = this.data ?? [];\n const values = rows.map((row) => {\n const num = Number(row?.[this.valueKey]);\n return Number.isFinite(num) ? num : 0;\n });\n const max = values.reduce((m, v) => Math.max(m, v), 0);\n\n return rows.map((row, i) => {\n const raw = row?.[this.labelKey];\n const value = values[i];\n return {\n label: raw == null ? '' : String(raw),\n value,\n percent: max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0,\n formattedValue: value.toLocaleString('en-US'),\n };\n });\n }\n\n get computedClass() {\n return cn('flex w-full flex-col gap-1', this.class);\n }\n}\n"
14
+ },
15
+ {
16
+ "path": "utils/cn.ts",
17
+ "target": "ui/utils/cn.ts",
18
+ "content": "import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "category-bar",
3
+ "type": "registry:ui",
4
+ "title": "Category Bar",
5
+ "description": "Category Bar component.",
6
+ "category": "components",
7
+ "registryDependencies": [],
8
+ "dependencies": [],
9
+ "files": [
10
+ {
11
+ "path": "category-bar.component.ts",
12
+ "target": "ui/category-bar.component.ts",
13
+ "content": "import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, OnChanges, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { cn } from './utils/cn';\n\n/** One resolved segment: its share of the bar's width, its paint, and the numbers behind it. */\nexport interface CategoryBarSegment {\n label: string;\n value: number;\n percent: number;\n color: string;\n}\n\n/**\n * A single bar split into proportional, labelled ranges — for showing where\n * one value falls against a scale made of categories (a credit score inside\n * \"Poor / Fair / Good / Excellent\", latency inside \"Fast / OK / Slow\").\n *\n * Segments take the chart palette by position, since a category bar has no\n * stable per-render identity to key colour off the way a chart series does —\n * pass `colors` to override.\n * @new\n */\n@Component({\n selector: 'tolle-category-bar',\n styles: [':host { display: block; }'],\n standalone: true,\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div [class]=\"computedClass\">\n <div class=\"relative flex w-full gap-0.5\">\n <div\n *ngFor=\"let segment of segments; let i = index; trackBy: trackByIndex\"\n class=\"h-2 first:rounded-l-full last:rounded-r-full\"\n [style.width.%]=\"segment.percent\"\n [style.background]=\"segment.color\"\n [attr.title]=\"segment.label || null\"\n ></div>\n\n <span\n *ngIf=\"clampedMarker !== null\"\n class=\"absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-foreground shadow\"\n [style.left.%]=\"clampedMarker\"\n ></span>\n </div>\n\n <p class=\"sr-only\">{{ summary }}</p>\n </div>\n `,\n})\nexport class CategoryBarComponent implements OnChanges {\n /**\n * Angular writes a bound `class` input through its styling path, which does\n * not mark an OnPush component dirty — without this hook the component keeps\n * rendering the class it was born with.\n */\n ngOnChanges(): void {\n this.cdr.markForCheck();\n }\n\n /** Identity for `*ngFor`, so a redraw patches nodes instead of replacing them. */\n trackByIndex = (index: number): number => index;\n\n /** Width of each segment. Values that are not finite and greater than zero are dropped. @default [] */\n @Input() values: number[] = [];\n /** Name for each segment, used in the accessible summary and as a title tooltip. @default [] */\n @Input() labels: string[] = [];\n /** Paint for each segment, by index. Falls back to the chart palette --chart-1 through --chart-5. @default [] */\n @Input() colors: string[] = [];\n /** Position of the pointer, 0-100 on the same scale the segments sum to. Omit to draw no pointer. @default null */\n @Input() markerValue: number | null = null;\n /** Accessible name, prefixed onto the sr-only summary of segments and marker. @default '' */\n @Input() ariaLabel = '';\n /** Extra Tailwind classes merged onto the bar via `cn()` (last-wins). */\n @Input() class = '';\n\n private readonly cdr = inject(ChangeDetectorRef);\n\n private get total(): number {\n return this.values.reduce((sum, v) => (Number.isFinite(v) && v > 0 ? sum + v : sum), 0);\n }\n\n get segments(): CategoryBarSegment[] {\n const total = this.total;\n if (total <= 0) return [];\n\n return this.values\n .map((value, i) => ({ value, i }))\n .filter(({ value }) => Number.isFinite(value) && value > 0)\n .map(({ value, i }) => ({\n label: this.labels[i] ?? '',\n value,\n percent: (value / total) * 100,\n color: this.colors[i] || 'rgb(var(--chart-' + ((i % 5) + 1) + '))',\n }));\n }\n\n get clampedMarker(): number | null {\n if (this.markerValue == null || !Number.isFinite(this.markerValue)) return null;\n return Math.min(100, Math.max(0, this.markerValue));\n }\n\n get summary(): string {\n const parts = this.segments.map((s) => (s.label ? s.label + ': ' : '') + s.percent.toFixed(0) + '%');\n const marker = this.clampedMarker !== null ? ', current position ' + this.clampedMarker.toFixed(0) + '%' : '';\n const prefix = this.ariaLabel ? this.ariaLabel + '. ' : '';\n return prefix + parts.join(', ') + marker;\n }\n\n get computedClass() {\n return cn('w-full', this.class);\n }\n}\n"
14
+ },
15
+ {
16
+ "path": "utils/cn.ts",
17
+ "target": "ui/utils/cn.ts",
18
+ "content": "import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"
19
+ }
20
+ ]
21
+ }