@joanroucoux/cairn-ui 0.8.2 → 0.9.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs +57 -53
  3. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs.map +1 -1
  4. package/fesm2022/joanroucoux-cairn-ui-alert.mjs +155 -47
  5. package/fesm2022/joanroucoux-cairn-ui-alert.mjs.map +1 -1
  6. package/fesm2022/joanroucoux-cairn-ui-amount.mjs +220 -110
  7. package/fesm2022/joanroucoux-cairn-ui-amount.mjs.map +1 -1
  8. package/fesm2022/joanroucoux-cairn-ui-async.mjs +392 -216
  9. package/fesm2022/joanroucoux-cairn-ui-async.mjs.map +1 -1
  10. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs +145 -68
  11. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs.map +1 -1
  12. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs +87 -47
  13. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs.map +1 -1
  14. package/fesm2022/joanroucoux-cairn-ui-badge.mjs +83 -43
  15. package/fesm2022/joanroucoux-cairn-ui-badge.mjs.map +1 -1
  16. package/fesm2022/joanroucoux-cairn-ui-button.mjs +184 -104
  17. package/fesm2022/joanroucoux-cairn-ui-button.mjs.map +1 -1
  18. package/fesm2022/joanroucoux-cairn-ui-card.mjs +232 -137
  19. package/fesm2022/joanroucoux-cairn-ui-card.mjs.map +1 -1
  20. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs +298 -102
  21. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs.map +1 -1
  22. package/fesm2022/joanroucoux-cairn-ui-control.mjs +4 -17
  23. package/fesm2022/joanroucoux-cairn-ui-control.mjs.map +1 -1
  24. package/fesm2022/joanroucoux-cairn-ui-delta.mjs +150 -83
  25. package/fesm2022/joanroucoux-cairn-ui-delta.mjs.map +1 -1
  26. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs +508 -471
  27. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs.map +1 -1
  28. package/fesm2022/joanroucoux-cairn-ui-donut.mjs +255 -157
  29. package/fesm2022/joanroucoux-cairn-ui-donut.mjs.map +1 -1
  30. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs +303 -0
  31. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs.map +1 -0
  32. package/fesm2022/joanroucoux-cairn-ui-empty.mjs +76 -35
  33. package/fesm2022/joanroucoux-cairn-ui-empty.mjs.map +1 -1
  34. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs +40 -31
  35. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs.map +1 -1
  36. package/fesm2022/joanroucoux-cairn-ui-fact.mjs +188 -79
  37. package/fesm2022/joanroucoux-cairn-ui-fact.mjs.map +1 -1
  38. package/fesm2022/joanroucoux-cairn-ui-field.mjs +309 -173
  39. package/fesm2022/joanroucoux-cairn-ui-field.mjs.map +1 -1
  40. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs +165 -87
  41. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs.map +1 -1
  42. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs +269 -0
  43. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs.map +1 -0
  44. package/fesm2022/joanroucoux-cairn-ui-input.mjs +205 -85
  45. package/fesm2022/joanroucoux-cairn-ui-input.mjs.map +1 -1
  46. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs +838 -512
  47. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs.map +1 -1
  48. package/fesm2022/joanroucoux-cairn-ui-menu.mjs +401 -225
  49. package/fesm2022/joanroucoux-cairn-ui-menu.mjs.map +1 -1
  50. package/fesm2022/joanroucoux-cairn-ui-meter.mjs +143 -57
  51. package/fesm2022/joanroucoux-cairn-ui-meter.mjs.map +1 -1
  52. package/fesm2022/joanroucoux-cairn-ui-motion.mjs +367 -399
  53. package/fesm2022/joanroucoux-cairn-ui-motion.mjs.map +1 -1
  54. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs +73 -44
  55. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs.map +1 -1
  56. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs +191 -0
  57. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs.map +1 -0
  58. package/fesm2022/joanroucoux-cairn-ui-row.mjs +376 -146
  59. package/fesm2022/joanroucoux-cairn-ui-row.mjs.map +1 -1
  60. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs +183 -81
  61. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs.map +1 -1
  62. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs +233 -0
  63. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs.map +1 -0
  64. package/fesm2022/joanroucoux-cairn-ui-select.mjs +121 -67
  65. package/fesm2022/joanroucoux-cairn-ui-select.mjs.map +1 -1
  66. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs +129 -51
  67. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs.map +1 -1
  68. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs +80 -39
  69. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs.map +1 -1
  70. package/fesm2022/joanroucoux-cairn-ui-stat.mjs +85 -55
  71. package/fesm2022/joanroucoux-cairn-ui-stat.mjs.map +1 -1
  72. package/fesm2022/joanroucoux-cairn-ui-switch.mjs +48 -35
  73. package/fesm2022/joanroucoux-cairn-ui-switch.mjs.map +1 -1
  74. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs +113 -69
  75. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs.map +1 -1
  76. package/fesm2022/joanroucoux-cairn-ui-table.mjs +1036 -310
  77. package/fesm2022/joanroucoux-cairn-ui-table.mjs.map +1 -1
  78. package/fesm2022/joanroucoux-cairn-ui-toast.mjs +240 -127
  79. package/fesm2022/joanroucoux-cairn-ui-toast.mjs.map +1 -1
  80. package/fesm2022/joanroucoux-cairn-ui.mjs +0 -4
  81. package/package.json +17 -1
  82. package/styles/motion.css +1 -13
  83. package/styles/tokens.css +35 -64
  84. package/types/joanroucoux-cairn-ui-action-bar.d.ts +9 -14
  85. package/types/joanroucoux-cairn-ui-alert.d.ts +29 -15
  86. package/types/joanroucoux-cairn-ui-amount.d.ts +64 -32
  87. package/types/joanroucoux-cairn-ui-async.d.ts +85 -38
  88. package/types/joanroucoux-cairn-ui-avatar.d.ts +34 -22
  89. package/types/joanroucoux-cairn-ui-back-link.d.ts +20 -15
  90. package/types/joanroucoux-cairn-ui-badge.d.ts +22 -14
  91. package/types/joanroucoux-cairn-ui-button.d.ts +36 -18
  92. package/types/joanroucoux-cairn-ui-card.d.ts +48 -23
  93. package/types/joanroucoux-cairn-ui-choice-chips.d.ts +91 -35
  94. package/types/joanroucoux-cairn-ui-control.d.ts +9 -18
  95. package/types/joanroucoux-cairn-ui-delta.d.ts +46 -24
  96. package/types/joanroucoux-cairn-ui-dialog.d.ts +124 -57
  97. package/types/joanroucoux-cairn-ui-donut.d.ts +83 -46
  98. package/types/joanroucoux-cairn-ui-drawer.d.ts +73 -0
  99. package/types/joanroucoux-cairn-ui-empty.d.ts +20 -13
  100. package/types/joanroucoux-cairn-ui-external-link.d.ts +6 -10
  101. package/types/joanroucoux-cairn-ui-fact.d.ts +49 -25
  102. package/types/joanroucoux-cairn-ui-field.d.ts +72 -49
  103. package/types/joanroucoux-cairn-ui-filter-chips.d.ts +35 -20
  104. package/types/joanroucoux-cairn-ui-group-header.d.ts +58 -0
  105. package/types/joanroucoux-cairn-ui-input.d.ts +59 -37
  106. package/types/joanroucoux-cairn-ui-line-chart.d.ts +199 -89
  107. package/types/joanroucoux-cairn-ui-menu.d.ts +81 -44
  108. package/types/joanroucoux-cairn-ui-meter.d.ts +44 -22
  109. package/types/joanroucoux-cairn-ui-motion.d.ts +61 -75
  110. package/types/joanroucoux-cairn-ui-nav-item.d.ts +15 -13
  111. package/types/joanroucoux-cairn-ui-result-group.d.ts +52 -0
  112. package/types/joanroucoux-cairn-ui-row.d.ts +79 -37
  113. package/types/joanroucoux-cairn-ui-segmented.d.ts +51 -27
  114. package/types/joanroucoux-cairn-ui-select-pill.d.ts +52 -0
  115. package/types/joanroucoux-cairn-ui-select.d.ts +28 -18
  116. package/types/joanroucoux-cairn-ui-skeleton.d.ts +35 -18
  117. package/types/joanroucoux-cairn-ui-stale-link.d.ts +23 -16
  118. package/types/joanroucoux-cairn-ui-stat.d.ts +26 -21
  119. package/types/joanroucoux-cairn-ui-switch.d.ts +7 -11
  120. package/types/joanroucoux-cairn-ui-tab-bar.d.ts +20 -17
  121. package/types/joanroucoux-cairn-ui-table.d.ts +251 -89
  122. package/types/joanroucoux-cairn-ui-toast.d.ts +46 -36
  123. package/types/joanroucoux-cairn-ui.d.ts +1 -2
  124. package/fesm2022/joanroucoux-cairn-ui.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"joanroucoux-cairn-ui-donut.mjs","sources":["../../../projects/ui/donut/internal/donut-geometry.ts","../../../projects/ui/donut/internal/donut-slices.ts","../../../projects/ui/donut/donut.ts","../../../projects/ui/donut/joanroucoux-cairn-ui-donut.ts"],"sourcesContent":["import type { RankedSlice } from './donut-slices';\n\nexport type PositionedSlice = RankedSlice & { dash: string; offset: string };\n\nexport const RADIUS = 78;\nexport const RING_WIDTH = 30;\nexport const CIRCUMFERENCE = 2 * Math.PI * RADIUS;\n\nconst GAP = 2;\n\nexport function positionSlices(slices: RankedSlice[]): PositionedSlice[] {\n let travelled = 0;\n\n return slices.map((slice) => {\n const length = slice.share * CIRCUMFERENCE;\n const drawn = Math.max(0, length - GAP);\n const positioned = {\n ...slice,\n dash: `${drawn.toFixed(2)} ${(CIRCUMFERENCE - drawn).toFixed(2)}`,\n offset: (-travelled).toFixed(2),\n };\n travelled += length;\n\n return positioned;\n });\n}\n","export type DonutSlice = {\n id: string;\n label: string;\n value: number;\n sublabel?: string;\n};\n\nexport type Ramp = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport type SharedSlice = DonutSlice & { share: number };\n\nexport type RankedSlice = DonutSlice & { share: number; ramp: Ramp; members: string[] };\n\nconst MAX_RAMP = 6;\nconst OTHERS_ID = 'others';\n\nexport function shareSlices(slices: DonutSlice[]): SharedSlice[] {\n const positive = slices.filter((slice) => slice.value > 0).sort((a, b) => b.value - a.value);\n const total = positive.reduce((sum, slice) => sum + slice.value, 0);\n\n return positive.map((slice) => ({ ...slice, share: slice.value / total }));\n}\n\nexport function rankSlices(slices: DonutSlice[], othersLabel: string): RankedSlice[] {\n const shared = shareSlices(slices);\n\n const toRanked = (slice: SharedSlice, ramp: Ramp, members: string[] = []): RankedSlice => ({\n ...slice,\n ramp,\n members,\n });\n\n if (shared.length <= MAX_RAMP) {\n return shared.map((slice, index) => toRanked(slice, (index + 1) as Ramp));\n }\n\n const kept = shared.slice(0, MAX_RAMP - 1);\n const rest = shared.slice(MAX_RAMP - 1);\n const othersValue = rest.reduce((sum, slice) => sum + slice.value, 0);\n const othersShare = rest.reduce((sum, slice) => sum + slice.share, 0);\n\n return [\n ...kept.map((slice, index) => toRanked(slice, (index + 1) as Ramp)),\n toRanked(\n { id: OTHERS_ID, label: othersLabel, value: othersValue, share: othersShare },\n MAX_RAMP,\n rest.map((slice) => slice.label),\n ),\n ];\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { Component, computed, input, output, signal } from '@angular/core';\n\nimport { RADIUS, RING_WIDTH, positionSlices } from './internal/donut-geometry';\nimport { type DonutSlice, type Ramp, type RankedSlice, rankSlices, shareSlices } from './internal/donut-slices';\n\nexport type { DonutSlice };\n\nconst identityValue = (value: number): string => `${value}`;\nconst identityShare = (share: number): string => `${Math.round(share * 100)}%`;\n\nconst ROW_CLASSES =\n 'flex w-full items-center gap-3 min-h-14 px-2 py-1 [font-variant-numeric:var(--numeric)] rounded-control text-left transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) active:bg-(--soft) active:scale-(--press-scale) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';\n\nconst SLICE_CLASSES =\n 'cursor-pointer origin-center [transform-box:view-box] transition-[scale,opacity] duration-(--duration-fast) ease-out';\n\nconst RAMP_DOT_CLASSES: Record<Ramp, string> = {\n 1: 'bg-(--ramp-1)',\n 2: 'bg-(--ramp-2)',\n 3: 'bg-(--ramp-3)',\n 4: 'bg-(--ramp-4)',\n 5: 'bg-(--ramp-5)',\n 6: 'bg-(--ramp-6)',\n};\n\n/**\n * A ring chart with a legend: slices rank from largest to smallest on a grey ramp, and hovering,\n * focusing or activating a legend row highlights its slice and centres it in the ring.\n *\n * @example\n * <ui-donut [slices]=\"byAssetClass()\" label=\"Par classe d'actif\" othersLabel=\"Autres\" [valueFormat]=\"formatEur\" [shareFormat]=\"formatShare\" (sliceSelect)=\"goToHoldings($event)\" />\n */\n@Component({\n selector: 'ui-donut',\n template: `\n <div class=\"relative size-[232px] flex-none sm:size-60\">\n <svg class=\"block h-full w-full -rotate-90\" role=\"img\" viewBox=\"0 0 200 200\" [attr.aria-label]=\"ariaLabel()\">\n <circle\n class=\"stroke-(--muted)\"\n cx=\"100\"\n cy=\"100\"\n fill=\"none\"\n [attr.r]=\"radius\"\n [attr.stroke-width]=\"ringWidth\"\n />\n @for (slice of positioned(); track slice.id) {\n <circle\n cx=\"100\"\n cy=\"100\"\n data-slice\n fill=\"none\"\n [attr.r]=\"radius\"\n [attr.stroke-dasharray]=\"slice.dash\"\n [attr.stroke-dashoffset]=\"slice.offset\"\n [attr.stroke-width]=\"ringWidth\"\n [class]=\"sliceClasses(slice)\"\n [style.stroke]=\"strokeColor(slice)\"\n (click)=\"select(slice)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n />\n }\n </svg>\n\n @if (active(); as slice) {\n <div\n class=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center px-12 text-center\"\n data-testid=\"donut-centre\"\n >\n <span class=\"text-label max-w-full truncate font-medium whitespace-nowrap text-(--muted-foreground)\">{{\n slice.label\n }}</span>\n <span class=\"text-heading font-semibold tracking-(--tracking-display)\">{{ shareFormat()(slice.share) }}</span>\n <span class=\"text-caption tracking-normal text-(--muted-foreground) [font-variant-numeric:var(--numeric)]\">{{\n valueFormat()(slice.value)\n }}</span>\n </div>\n }\n </div>\n\n <div class=\"flex w-full min-w-0 flex-1 flex-col sm:w-auto\" data-testid=\"donut-legend\">\n @for (slice of ranked(); track slice.id) {\n @if (hrefFor(slice.id); as href) {\n <a\n [attr.href]=\"href\"\n [class]=\"rowClasses(slice)\"\n (blur)=\"clearHighlight(slice.id)\"\n (click)=\"follow($event, slice)\"\n (focus)=\"highlight(slice.id)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: slice }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClasses(slice)\"\n (blur)=\"clearHighlight(slice.id)\"\n (click)=\"select(slice)\"\n (focus)=\"highlight(slice.id)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: slice }\" />\n </button>\n }\n }\n </div>\n\n <ng-template #rowContent let-slice>\n <span [class]=\"dotClasses(slice)\"></span>\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"text-body truncate font-medium\">{{ slice.label }}</span>\n <span class=\"text-caption truncate tracking-(--tracking-caption) text-(--subtle-foreground)\">{{\n subLabel(slice)\n }}</span>\n </span>\n <span class=\"flex flex-none flex-col items-end whitespace-nowrap\">\n <span class=\"text-body font-medium\">{{ shareFormat()(slice.share) }}</span>\n <span class=\"text-caption tracking-normal text-(--muted-foreground)\">{{ valueFormat()(slice.value) }}</span>\n </span>\n </ng-template>\n `,\n imports: [NgTemplateOutlet],\n host: {\n class: 'flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8',\n },\n})\nexport class UiDonut {\n readonly slices = input.required<DonutSlice[]>();\n readonly label = input.required<string>();\n readonly othersLabel = input.required<string>();\n readonly valueFormat = input<(value: number) => string>(identityValue);\n readonly shareFormat = input<(share: number) => string>(identityShare);\n\n readonly legendHref = input<((id: string) => string | null) | undefined>();\n\n readonly sliceSelect = output<DonutSlice['id']>();\n\n protected readonly activeId = signal<string | null>(null);\n\n protected readonly ranked = computed(() => rankSlices(this.slices(), this.othersLabel()));\n protected readonly positioned = computed(() => positionSlices(this.ranked()));\n\n protected readonly active = computed<RankedSlice | null>(() => {\n const ranked = this.ranked();\n const id = this.activeId();\n\n return ranked.find((slice) => slice.id === id) ?? ranked[0] ?? null;\n });\n\n protected readonly ariaLabel = computed(() => {\n const detail = shareSlices(this.slices())\n .map((slice) => `${slice.label} ${this.shareFormat()(slice.share)}`)\n .join(', ');\n\n return detail ? `${this.label()} : ${detail}` : this.label();\n });\n\n protected readonly radius = RADIUS;\n protected readonly ringWidth = RING_WIDTH;\n\n protected strokeColor(slice: RankedSlice): string {\n return `var(--ramp-${slice.ramp})`;\n }\n\n protected sliceClasses(slice: RankedSlice): string {\n const id = this.activeId();\n if (id === null) {\n return SLICE_CLASSES;\n }\n\n return `${SLICE_CLASSES} ${id === slice.id ? 'scale-104 motion-reduce:scale-100' : 'opacity-50'}`;\n }\n\n protected dotClasses(slice: RankedSlice): string {\n return `size-3 flex-none rounded-pill shadow-[inset_0_0_0_1px_var(--border)] ${RAMP_DOT_CLASSES[slice.ramp]}`;\n }\n\n protected rowClasses(slice: RankedSlice): string {\n return `${ROW_CLASSES}${this.isActive(slice) ? ' bg-(--soft)' : ''}`;\n }\n\n protected subLabel(slice: RankedSlice): string {\n return slice.members.length > 0 ? slice.members.join(', ') : (slice.sublabel ?? '');\n }\n\n protected isActive(slice: RankedSlice): boolean {\n return this.active()?.id === slice.id;\n }\n\n protected highlight(id: string): void {\n this.activeId.set(id);\n }\n\n protected clearHighlight(id: string): void {\n if (this.activeId() === id) {\n this.activeId.set(null);\n }\n }\n\n protected hrefFor(id: string): string | null {\n return this.legendHref()?.(id) ?? null;\n }\n\n protected follow(event: MouseEvent, slice: RankedSlice): void {\n if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {\n return;\n }\n event.preventDefault();\n this.select(slice);\n }\n\n protected select(slice: RankedSlice): void {\n this.activeId.set(slice.id);\n this.sliceSelect.emit(slice.id);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"joanroucoux-cairn-ui-donut.mjs","names":[],"sources":["../../../projects/ui/donut/internal/donut-geometry.ts","../../../projects/ui/donut/internal/donut-slices.ts","../../../projects/ui/donut/donut.ts"],"sourcesContent":["import type { RankedSlice } from './donut-slices';\n\nexport type PositionedSlice = RankedSlice & { dash: string; offset: string };\n\nexport const RADIUS = 78;\nexport const RING_WIDTH = 30;\nexport const CIRCUMFERENCE = 2 * Math.PI * RADIUS;\n\nconst GAP = 2;\n\nexport function positionSlices(slices: RankedSlice[]): PositionedSlice[] {\n let travelled = 0;\n\n return slices.map((slice) => {\n const length = slice.share * CIRCUMFERENCE;\n const drawn = Math.max(0, length - GAP);\n const positioned = {\n ...slice,\n dash: `${drawn.toFixed(2)} ${(CIRCUMFERENCE - drawn).toFixed(2)}`,\n offset: (-travelled).toFixed(2),\n };\n travelled += length;\n\n return positioned;\n });\n}\n","export type DonutSlice = {\n id: string;\n label: string;\n value: number;\n sublabel?: string;\n};\n\nexport type Ramp = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport type SharedSlice = DonutSlice & { share: number };\n\nexport type RankedSlice = DonutSlice & { share: number; ramp: Ramp; members: string[] };\n\nconst MAX_RAMP = 6;\nconst OTHERS_ID = 'others';\n\nexport function shareSlices(slices: DonutSlice[]): SharedSlice[] {\n const positive = slices.filter((slice) => slice.value > 0).sort((a, b) => b.value - a.value);\n const total = positive.reduce((sum, slice) => sum + slice.value, 0);\n\n return positive.map((slice) => ({ ...slice, share: slice.value / total }));\n}\n\nexport function rankSlices(slices: DonutSlice[], othersLabel: string): RankedSlice[] {\n const shared = shareSlices(slices);\n\n const toRanked = (slice: SharedSlice, ramp: Ramp, members: string[] = []): RankedSlice => ({\n ...slice,\n ramp,\n members,\n });\n\n if (shared.length <= MAX_RAMP) {\n return shared.map((slice, index) => toRanked(slice, (index + 1) as Ramp));\n }\n\n const kept = shared.slice(0, MAX_RAMP - 1);\n const rest = shared.slice(MAX_RAMP - 1);\n const othersValue = rest.reduce((sum, slice) => sum + slice.value, 0);\n const othersShare = rest.reduce((sum, slice) => sum + slice.share, 0);\n\n return [\n ...kept.map((slice, index) => toRanked(slice, (index + 1) as Ramp)),\n toRanked(\n { id: OTHERS_ID, label: othersLabel, value: othersValue, share: othersShare },\n MAX_RAMP,\n rest.map((slice) => slice.label),\n ),\n ];\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { Component, computed, input, output, signal } from '@angular/core';\n\nimport { RADIUS, RING_WIDTH, positionSlices } from './internal/donut-geometry';\nimport { type DonutSlice, type Ramp, type RankedSlice, rankSlices, shareSlices } from './internal/donut-slices';\n\nexport type { DonutSlice };\n\nconst identityValue = (value: number): string => `${value}`;\nconst identityShare = (share: number): string => `${Math.round(share * 100)}%`;\n\nconst ROW_CLASSES =\n 'flex w-full items-center gap-3 min-h-14 px-2 py-1 [font-variant-numeric:var(--numeric)] rounded-control text-left transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) active:bg-(--soft) active:scale-(--press-scale) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';\n\nconst SLICE_CLASSES =\n 'cursor-pointer origin-center [transform-box:view-box] transition-[scale,opacity] duration-(--duration-fast) ease-out';\n\nconst RAMP_DOT_CLASSES: Record<Ramp, string> = {\n 1: 'bg-(--ramp-1)',\n 2: 'bg-(--ramp-2)',\n 3: 'bg-(--ramp-3)',\n 4: 'bg-(--ramp-4)',\n 5: 'bg-(--ramp-5)',\n 6: 'bg-(--ramp-6)',\n};\n\n/**\n * A ring chart with a legend: slices rank from largest to smallest, and hovering, focusing or\n * activating a legend row highlights its slice.\n *\n * @example\n * <ui-donut [slices]=\"byAssetClass()\" label=\"Par classe d'actif\" othersLabel=\"Autres\" [valueFormat]=\"formatEur\" [shareFormat]=\"formatShare\" (sliceSelect)=\"goToHoldings($event)\" />\n */\n@Component({\n selector: 'ui-donut',\n template: `\n <div class=\"relative size-[232px] flex-none sm:size-60\">\n <svg class=\"block h-full w-full -rotate-90\" role=\"img\" viewBox=\"0 0 200 200\" [attr.aria-label]=\"ariaLabel()\">\n <circle\n class=\"stroke-(--muted)\"\n cx=\"100\"\n cy=\"100\"\n fill=\"none\"\n [attr.r]=\"radius\"\n [attr.stroke-width]=\"ringWidth\"\n />\n @for (slice of positioned(); track slice.id) {\n <circle\n cx=\"100\"\n cy=\"100\"\n data-slice\n fill=\"none\"\n [attr.r]=\"radius\"\n [attr.stroke-dasharray]=\"slice.dash\"\n [attr.stroke-dashoffset]=\"slice.offset\"\n [attr.stroke-width]=\"ringWidth\"\n [class]=\"sliceClasses(slice)\"\n [style.stroke]=\"strokeColor(slice)\"\n (click)=\"select(slice)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n />\n }\n </svg>\n\n @if (active(); as slice) {\n <div\n class=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center px-12 text-center\"\n data-testid=\"donut-centre\"\n >\n <span class=\"text-label max-w-full truncate font-medium whitespace-nowrap text-(--muted-foreground)\">{{\n slice.label\n }}</span>\n <span class=\"text-heading font-semibold tracking-(--tracking-display)\">{{ shareFormat()(slice.share) }}</span>\n <span class=\"text-caption tracking-normal text-(--muted-foreground) [font-variant-numeric:var(--numeric)]\">{{\n valueFormat()(slice.value)\n }}</span>\n </div>\n }\n </div>\n\n <div class=\"flex w-full min-w-0 flex-1 flex-col sm:w-auto\" data-testid=\"donut-legend\">\n @for (slice of ranked(); track slice.id) {\n @if (hrefFor(slice.id); as href) {\n <a\n [attr.href]=\"href\"\n [class]=\"rowClasses(slice)\"\n (blur)=\"clearHighlight(slice.id)\"\n (click)=\"follow($event, slice)\"\n (focus)=\"highlight(slice.id)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: slice }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClasses(slice)\"\n (blur)=\"clearHighlight(slice.id)\"\n (click)=\"select(slice)\"\n (focus)=\"highlight(slice.id)\"\n (pointerenter)=\"highlight(slice.id)\"\n (pointerleave)=\"clearHighlight(slice.id)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: slice }\" />\n </button>\n }\n }\n </div>\n\n <ng-template #rowContent let-slice>\n <span [class]=\"dotClasses(slice)\"></span>\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"text-body truncate font-medium\">{{ slice.label }}</span>\n <span class=\"text-caption truncate tracking-(--tracking-caption) text-(--subtle-foreground)\">{{\n subLabel(slice)\n }}</span>\n </span>\n <span class=\"flex flex-none flex-col items-end whitespace-nowrap\">\n <span class=\"text-body font-medium\">{{ shareFormat()(slice.share) }}</span>\n <span class=\"text-caption tracking-normal text-(--muted-foreground)\">{{ valueFormat()(slice.value) }}</span>\n </span>\n </ng-template>\n `,\n imports: [NgTemplateOutlet],\n host: {\n class: 'flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8',\n },\n})\nexport class UiDonut {\n readonly slices = input.required<DonutSlice[]>();\n readonly label = input.required<string>();\n readonly othersLabel = input.required<string>();\n readonly valueFormat = input<(value: number) => string>(identityValue);\n readonly shareFormat = input<(share: number) => string>(identityShare);\n\n readonly legendHref = input<((id: string) => string | null) | undefined>();\n\n readonly sliceSelect = output<DonutSlice['id']>();\n\n protected readonly activeId = signal<string | null>(null);\n\n protected readonly ranked = computed(() => rankSlices(this.slices(), this.othersLabel()));\n protected readonly positioned = computed(() => positionSlices(this.ranked()));\n\n protected readonly active = computed<RankedSlice | null>(() => {\n const ranked = this.ranked();\n const id = this.activeId();\n\n return ranked.find((slice) => slice.id === id) ?? ranked[0] ?? null;\n });\n\n protected readonly ariaLabel = computed(() => {\n const detail = shareSlices(this.slices())\n .map((slice) => `${slice.label} ${this.shareFormat()(slice.share)}`)\n .join(', ');\n\n return detail ? `${this.label()} : ${detail}` : this.label();\n });\n\n protected readonly radius = RADIUS;\n protected readonly ringWidth = RING_WIDTH;\n\n protected strokeColor(slice: RankedSlice): string {\n return `var(--ramp-${slice.ramp})`;\n }\n\n protected sliceClasses(slice: RankedSlice): string {\n const id = this.activeId();\n if (id === null) {\n return SLICE_CLASSES;\n }\n\n return `${SLICE_CLASSES} ${id === slice.id ? 'scale-104 motion-reduce:scale-100' : 'opacity-50'}`;\n }\n\n protected dotClasses(slice: RankedSlice): string {\n return `size-3 flex-none rounded-pill shadow-[inset_0_0_0_1px_var(--border)] ${RAMP_DOT_CLASSES[slice.ramp]}`;\n }\n\n protected rowClasses(slice: RankedSlice): string {\n return `${ROW_CLASSES}${this.isActive(slice) ? ' bg-(--soft)' : ''}`;\n }\n\n protected subLabel(slice: RankedSlice): string {\n return slice.members.length > 0 ? slice.members.join(', ') : (slice.sublabel ?? '');\n }\n\n protected isActive(slice: RankedSlice): boolean {\n return this.active()?.id === slice.id;\n }\n\n protected highlight(id: string): void {\n this.activeId.set(id);\n }\n\n protected clearHighlight(id: string): void {\n if (this.activeId() === id) {\n this.activeId.set(null);\n }\n }\n\n protected hrefFor(id: string): string | null {\n return this.legendHref()?.(id) ?? null;\n }\n\n protected follow(event: MouseEvent, slice: RankedSlice): void {\n if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {\n return;\n }\n event.preventDefault();\n this.select(slice);\n }\n\n protected select(slice: RankedSlice): void {\n this.activeId.set(slice.id);\n this.sliceSelect.emit(slice.id);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,303 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, booleanAttribute, computed, input, output } from "@angular/core";
3
+ import { ModalClose } from "@joanroucoux/cairn-ui/dialog";
4
+ const DRAWER_CLOSE_REASONS = [
5
+ "escape",
6
+ "backdrop",
7
+ "cross",
8
+ "programmatic"
9
+ ];
10
+ const nextId = (() => {
11
+ let count = 0;
12
+ return () => `ui-drawer-${++count}`;
13
+ })();
14
+ var UiDrawer = class UiDrawer {
15
+ heading = input(...ngDevMode ? [void 0, { debugName: "heading" }] : /* istanbul ignore next */ []);
16
+ description = input(...ngDevMode ? [void 0, { debugName: "description" }] : /* istanbul ignore next */ []);
17
+ closeLabel = input(...ngDevMode ? [void 0, { debugName: "closeLabel" }] : /* istanbul ignore next */ []);
18
+ label = input(...ngDevMode ? [void 0, { debugName: "label" }] : /* istanbul ignore next */ []);
19
+ width = input("440px", ...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
20
+ open = input(false, {
21
+ ...ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {},
22
+ transform: booleanAttribute
23
+ });
24
+ busy = input(false, {
25
+ ...ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {},
26
+ transform: booleanAttribute
27
+ });
28
+ dismissed = output();
29
+ closed = output();
30
+ headingId = nextId();
31
+ descriptionId = `${this.headingId}-description`;
32
+ describedBy = computed(() => this.heading() && this.description() ? this.descriptionId : null, ...ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []);
33
+ ariaLabel = computed(() => {
34
+ if (this.heading()) return null;
35
+ if (!this.label()) throw new Error("ui-drawer needs a heading or a label: without either it has no accessible name.");
36
+ return this.label();
37
+ }, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
38
+ modal = new ModalClose({
39
+ open: this.open,
40
+ busy: this.busy,
41
+ dismissed: this.dismissed,
42
+ closed: this.closed
43
+ });
44
+ static ɵfac = i0.ɵɵngDeclareFactory({
45
+ minVersion: "12.0.0",
46
+ version: "22.2.0",
47
+ ngImport: i0,
48
+ type: UiDrawer,
49
+ deps: [],
50
+ target: i0.ɵɵFactoryTarget.Component
51
+ });
52
+ static ɵcmp = i0.ɵɵngDeclareComponent({
53
+ minVersion: "17.0.0",
54
+ version: "22.2.0",
55
+ type: UiDrawer,
56
+ isStandalone: true,
57
+ selector: "ui-drawer",
58
+ inputs: {
59
+ heading: {
60
+ classPropertyName: "heading",
61
+ publicName: "heading",
62
+ isSignal: true,
63
+ isRequired: false,
64
+ transformFunction: null
65
+ },
66
+ description: {
67
+ classPropertyName: "description",
68
+ publicName: "description",
69
+ isSignal: true,
70
+ isRequired: false,
71
+ transformFunction: null
72
+ },
73
+ closeLabel: {
74
+ classPropertyName: "closeLabel",
75
+ publicName: "closeLabel",
76
+ isSignal: true,
77
+ isRequired: false,
78
+ transformFunction: null
79
+ },
80
+ label: {
81
+ classPropertyName: "label",
82
+ publicName: "label",
83
+ isSignal: true,
84
+ isRequired: false,
85
+ transformFunction: null
86
+ },
87
+ width: {
88
+ classPropertyName: "width",
89
+ publicName: "width",
90
+ isSignal: true,
91
+ isRequired: false,
92
+ transformFunction: null
93
+ },
94
+ open: {
95
+ classPropertyName: "open",
96
+ publicName: "open",
97
+ isSignal: true,
98
+ isRequired: false,
99
+ transformFunction: null
100
+ },
101
+ busy: {
102
+ classPropertyName: "busy",
103
+ publicName: "busy",
104
+ isSignal: true,
105
+ isRequired: false,
106
+ transformFunction: null
107
+ }
108
+ },
109
+ outputs: {
110
+ dismissed: "dismissed",
111
+ closed: "closed"
112
+ },
113
+ host: { listeners: {
114
+ "pointerdown": "modal.onPointerDown($event)",
115
+ "click": "modal.onClick($event)",
116
+ "keydown": "modal.onKeydown($event)"
117
+ } },
118
+ ngImport: i0,
119
+ template: `
120
+ <dialog
121
+ class="fixed inset-y-0 right-0 left-auto m-0 h-dvh max-h-none w-(--drawer-width) max-w-full [scrollbar-width:none] flex-col gap-6 overflow-y-auto overscroll-contain bg-(--card) p-6 text-(--foreground) shadow-[-1px_0_0_var(--border),-24px_0_48px_rgb(0_0_0/0.12)] backdrop:bg-black/[0.36] open:flex"
122
+ [attr.aria-busy]="busy() || null"
123
+ [attr.aria-describedby]="describedBy()"
124
+ [attr.aria-label]="ariaLabel()"
125
+ [attr.aria-labelledby]="heading() ? headingId : null"
126
+ [style.--drawer-width]="width()"
127
+ (cancel)="modal.onCancel($event)"
128
+ (close)="modal.onNativeClose()"
129
+ >
130
+ @if (heading()) {
131
+ <div class="flex items-start justify-between gap-3">
132
+ <div class="flex min-w-0 flex-col gap-0.5">
133
+ <h2 class="text-title font-semibold text-pretty" [id]="headingId">{{ heading() }}</h2>
134
+
135
+ @if (description()) {
136
+ <p class="text-label text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
137
+ }
138
+ </div>
139
+
140
+ @if (closeLabel()) {
141
+ <button
142
+ class="rounded-control -mt-1 -mr-2 grid size-9 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale] duration-(--duration-press) ease-out outline-none hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale) disabled:pointer-events-none disabled:opacity-40"
143
+ type="button"
144
+ [attr.aria-label]="closeLabel()"
145
+ [disabled]="busy()"
146
+ (click)="modal.close('cross')"
147
+ >
148
+ <svg
149
+ aria-hidden="true"
150
+ class="block size-5 fill-none stroke-current stroke-[1.75]"
151
+ stroke-linecap="round"
152
+ stroke-linejoin="round"
153
+ viewBox="0 0 24 24"
154
+ >
155
+ <path d="M18 6 6 18" />
156
+ <path d="m6 6 12 12" />
157
+ </svg>
158
+ </button>
159
+ }
160
+ </div>
161
+ }
162
+
163
+ <ng-content />
164
+ </dialog>
165
+ `,
166
+ isInline: true,
167
+ styles: ["dialog{transition:opacity var(--duration-base) var(--ease-out),transform var(--duration-base) var(--ease-out),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:none}dialog:not([open]){opacity:0;transform:translate(24px);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{opacity:0;transform:translate(24px)}}@media(prefers-reduced-motion:reduce){dialog:not([open]){transform:none}@starting-style{dialog[open]{transform:none}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"]
168
+ });
169
+ };
170
+ i0.ɵɵngDeclareClassMetadata({
171
+ minVersion: "12.0.0",
172
+ version: "22.2.0",
173
+ ngImport: i0,
174
+ type: UiDrawer,
175
+ decorators: [{
176
+ type: Component,
177
+ args: [{
178
+ selector: "ui-drawer",
179
+ host: {
180
+ "(pointerdown)": "modal.onPointerDown($event)",
181
+ "(click)": "modal.onClick($event)",
182
+ "(keydown)": "modal.onKeydown($event)"
183
+ },
184
+ template: `
185
+ <dialog
186
+ class="fixed inset-y-0 right-0 left-auto m-0 h-dvh max-h-none w-(--drawer-width) max-w-full [scrollbar-width:none] flex-col gap-6 overflow-y-auto overscroll-contain bg-(--card) p-6 text-(--foreground) shadow-[-1px_0_0_var(--border),-24px_0_48px_rgb(0_0_0/0.12)] backdrop:bg-black/[0.36] open:flex"
187
+ [attr.aria-busy]="busy() || null"
188
+ [attr.aria-describedby]="describedBy()"
189
+ [attr.aria-label]="ariaLabel()"
190
+ [attr.aria-labelledby]="heading() ? headingId : null"
191
+ [style.--drawer-width]="width()"
192
+ (cancel)="modal.onCancel($event)"
193
+ (close)="modal.onNativeClose()"
194
+ >
195
+ @if (heading()) {
196
+ <div class="flex items-start justify-between gap-3">
197
+ <div class="flex min-w-0 flex-col gap-0.5">
198
+ <h2 class="text-title font-semibold text-pretty" [id]="headingId">{{ heading() }}</h2>
199
+
200
+ @if (description()) {
201
+ <p class="text-label text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
202
+ }
203
+ </div>
204
+
205
+ @if (closeLabel()) {
206
+ <button
207
+ class="rounded-control -mt-1 -mr-2 grid size-9 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale] duration-(--duration-press) ease-out outline-none hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale) disabled:pointer-events-none disabled:opacity-40"
208
+ type="button"
209
+ [attr.aria-label]="closeLabel()"
210
+ [disabled]="busy()"
211
+ (click)="modal.close('cross')"
212
+ >
213
+ <svg
214
+ aria-hidden="true"
215
+ class="block size-5 fill-none stroke-current stroke-[1.75]"
216
+ stroke-linecap="round"
217
+ stroke-linejoin="round"
218
+ viewBox="0 0 24 24"
219
+ >
220
+ <path d="M18 6 6 18" />
221
+ <path d="m6 6 12 12" />
222
+ </svg>
223
+ </button>
224
+ }
225
+ </div>
226
+ }
227
+
228
+ <ng-content />
229
+ </dialog>
230
+ `,
231
+ styles: ["dialog{transition:opacity var(--duration-base) var(--ease-out),transform var(--duration-base) var(--ease-out),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:none}dialog:not([open]){opacity:0;transform:translate(24px);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{opacity:0;transform:translate(24px)}}@media(prefers-reduced-motion:reduce){dialog:not([open]){transform:none}@starting-style{dialog[open]{transform:none}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"]
232
+ }]
233
+ }],
234
+ propDecorators: {
235
+ heading: [{
236
+ type: i0.Input,
237
+ args: [{
238
+ isSignal: true,
239
+ alias: "heading",
240
+ required: false
241
+ }]
242
+ }],
243
+ description: [{
244
+ type: i0.Input,
245
+ args: [{
246
+ isSignal: true,
247
+ alias: "description",
248
+ required: false
249
+ }]
250
+ }],
251
+ closeLabel: [{
252
+ type: i0.Input,
253
+ args: [{
254
+ isSignal: true,
255
+ alias: "closeLabel",
256
+ required: false
257
+ }]
258
+ }],
259
+ label: [{
260
+ type: i0.Input,
261
+ args: [{
262
+ isSignal: true,
263
+ alias: "label",
264
+ required: false
265
+ }]
266
+ }],
267
+ width: [{
268
+ type: i0.Input,
269
+ args: [{
270
+ isSignal: true,
271
+ alias: "width",
272
+ required: false
273
+ }]
274
+ }],
275
+ open: [{
276
+ type: i0.Input,
277
+ args: [{
278
+ isSignal: true,
279
+ alias: "open",
280
+ required: false
281
+ }]
282
+ }],
283
+ busy: [{
284
+ type: i0.Input,
285
+ args: [{
286
+ isSignal: true,
287
+ alias: "busy",
288
+ required: false
289
+ }]
290
+ }],
291
+ dismissed: [{
292
+ type: i0.Output,
293
+ args: ["dismissed"]
294
+ }],
295
+ closed: [{
296
+ type: i0.Output,
297
+ args: ["closed"]
298
+ }]
299
+ }
300
+ });
301
+ export { DRAWER_CLOSE_REASONS, UiDrawer };
302
+
303
+ //# sourceMappingURL=joanroucoux-cairn-ui-drawer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joanroucoux-cairn-ui-drawer.mjs","names":[],"sources":["../../../projects/ui/drawer/internal/drawer-styles.ts","../../../projects/ui/drawer/drawer.ts"],"sourcesContent":["export const DRAWER_STYLES = `\ndialog {\n transition:\n opacity var(--duration-base) var(--ease-out),\n transform var(--duration-base) var(--ease-out),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n opacity: 1;\n transform: none;\n}\n\ndialog:not([open]) {\n opacity: 0;\n transform: translateX(24px);\n transition-duration: var(--duration-exit);\n}\n\n@starting-style {\n dialog[open] {\n opacity: 0;\n transform: translateX(24px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog:not([open]) {\n transform: none;\n }\n\n @starting-style {\n dialog[open] {\n transform: none;\n }\n }\n}\n\ndialog::backdrop {\n transition: opacity var(--duration-base) var(--ease-out);\n opacity: 1;\n}\n\ndialog:not([open])::backdrop {\n opacity: 0;\n transition-duration: var(--duration-exit);\n}\n\n@starting-style {\n dialog[open]::backdrop {\n opacity: 0;\n }\n}\n`;\n","import { Component, booleanAttribute, computed, input, output } from '@angular/core';\n\nimport { ModalClose } from '@joanroucoux/cairn-ui/dialog';\n\nimport { DRAWER_STYLES } from './internal/drawer-styles';\n\n/** What started a close, as `closed` reports it. */\nexport const DRAWER_CLOSE_REASONS = ['escape', 'backdrop', 'cross', 'programmatic'] as const;\nexport type DrawerCloseReason = (typeof DRAWER_CLOSE_REASONS)[number];\n\nconst nextId = (() => {\n let count = 0;\n\n return () => `ui-drawer-${++count}`;\n})();\n\n/**\n * Modal side panel on the native <dialog>, against the right edge at full height, for a detail opened from a list.\n *\n * Without `heading` it draws no header: the content brings its own title and close button, and `label` names it.\n *\n * @example\n * <ui-drawer heading=\"Northwind Monde\" closeLabel=\"Fermer le détail\" [open]=\"open()\" (dismissed)=\"open.set(false)\">\n * <p>Body</p>\n * </ui-drawer>\n */\n@Component({\n selector: 'ui-drawer',\n host: {\n '(pointerdown)': 'modal.onPointerDown($event)',\n '(click)': 'modal.onClick($event)',\n '(keydown)': 'modal.onKeydown($event)',\n },\n template: `\n <dialog\n class=\"fixed inset-y-0 right-0 left-auto m-0 h-dvh max-h-none w-(--drawer-width) max-w-full [scrollbar-width:none] flex-col gap-6 overflow-y-auto overscroll-contain bg-(--card) p-6 text-(--foreground) shadow-[-1px_0_0_var(--border),-24px_0_48px_rgb(0_0_0/0.12)] backdrop:bg-black/[0.36] open:flex\"\n [attr.aria-busy]=\"busy() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"heading() ? headingId : null\"\n [style.--drawer-width]=\"width()\"\n (cancel)=\"modal.onCancel($event)\"\n (close)=\"modal.onNativeClose()\"\n >\n @if (heading()) {\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"flex min-w-0 flex-col gap-0.5\">\n <h2 class=\"text-title font-semibold text-pretty\" [id]=\"headingId\">{{ heading() }}</h2>\n\n @if (description()) {\n <p class=\"text-label text-(--muted-foreground)\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n\n @if (closeLabel()) {\n <button\n class=\"rounded-control -mt-1 -mr-2 grid size-9 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale] duration-(--duration-press) ease-out outline-none hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale) disabled:pointer-events-none disabled:opacity-40\"\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n [disabled]=\"busy()\"\n (click)=\"modal.close('cross')\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"block size-5 fill-none stroke-current stroke-[1.75]\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n </div>\n }\n\n <ng-content />\n </dialog>\n `,\n styles: DRAWER_STYLES,\n})\nexport class UiDrawer {\n readonly heading = input<string>();\n readonly description = input<string>();\n readonly closeLabel = input<string>();\n readonly label = input<string>();\n readonly width = input('440px');\n readonly open = input(false, { transform: booleanAttribute });\n readonly busy = input(false, { transform: booleanAttribute });\n readonly dismissed = output<void>();\n readonly closed = output<DrawerCloseReason>();\n\n protected readonly headingId = nextId();\n protected readonly descriptionId = `${this.headingId}-description`;\n\n protected readonly describedBy = computed(() => (this.heading() && this.description() ? this.descriptionId : null));\n\n protected readonly ariaLabel = computed(() => {\n if (this.heading()) {\n return null;\n }\n if (!this.label()) {\n throw new Error('ui-drawer needs a heading or a label: without either it has no accessible name.');\n }\n return this.label();\n });\n\n protected readonly modal = new ModalClose<DrawerCloseReason>({\n open: this.open,\n busy: this.busy,\n dismissed: this.dismissed,\n closed: this.closed,\n });\n}\n"],"mappings":"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"}
@@ -1,47 +1,88 @@
1
- import * as i0 from '@angular/core';
2
- import { input, Component } from '@angular/core';
3
-
4
- /**
5
- * Centred empty state: a title, a hint and an action slot, such as a catalogue search that matches
6
- * nothing. The padding is 24px, 48px from 64rem.
7
- *
8
- * @example
9
- * <ui-empty heading="Aucun instrument ne correspond à « zzz »" hint="La recherche porte sur le nom et l'ISIN.">
10
- * <button ui-button variant="outline" size="md" type="button">Ajouter une ligne</button>
11
- * </ui-empty>
12
- */
13
- class UiEmpty {
14
- heading = input.required(/* @ts-ignore */
15
- ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
16
- hint = input('', /* @ts-ignore */
17
- ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
18
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiEmpty, deps: [], target: i0.ɵɵFactoryTarget.Component });
19
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiEmpty, isStandalone: true, selector: "ui-empty", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12" }, ngImport: i0, template: `
1
+ import * as i0 from "@angular/core";
2
+ import { Component, input } from "@angular/core";
3
+ var UiEmpty = class UiEmpty {
4
+ heading = input.required(...ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []);
5
+ hint = input("", ...ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []);
6
+ static ɵfac = i0.ɵɵngDeclareFactory({
7
+ minVersion: "12.0.0",
8
+ version: "22.2.0",
9
+ ngImport: i0,
10
+ type: UiEmpty,
11
+ deps: [],
12
+ target: i0.ɵɵFactoryTarget.Component
13
+ });
14
+ static ɵcmp = i0.ɵɵngDeclareComponent({
15
+ minVersion: "17.0.0",
16
+ version: "22.2.0",
17
+ type: UiEmpty,
18
+ isStandalone: true,
19
+ selector: "ui-empty",
20
+ inputs: {
21
+ heading: {
22
+ classPropertyName: "heading",
23
+ publicName: "heading",
24
+ isSignal: true,
25
+ isRequired: true,
26
+ transformFunction: null
27
+ },
28
+ hint: {
29
+ classPropertyName: "hint",
30
+ publicName: "hint",
31
+ isSignal: true,
32
+ isRequired: false,
33
+ transformFunction: null
34
+ }
35
+ },
36
+ host: { classAttribute: "flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12" },
37
+ ngImport: i0,
38
+ template: `
20
39
  <span class="text-body font-medium text-pretty">{{ heading() }}</span>
21
40
  @if (hint()) {
22
41
  <span class="text-label text-(--muted-foreground)">{{ hint() }}</span>
23
42
  }
24
43
  <div class="mt-2 empty:hidden"><ng-content /></div>
25
- `, isInline: true });
26
- }
27
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiEmpty, decorators: [{
28
- type: Component,
29
- args: [{
30
- selector: 'ui-empty',
31
- template: `
44
+ `,
45
+ isInline: true
46
+ });
47
+ };
48
+ i0.ɵɵngDeclareClassMetadata({
49
+ minVersion: "12.0.0",
50
+ version: "22.2.0",
51
+ ngImport: i0,
52
+ type: UiEmpty,
53
+ decorators: [{
54
+ type: Component,
55
+ args: [{
56
+ selector: "ui-empty",
57
+ template: `
32
58
  <span class="text-body font-medium text-pretty">{{ heading() }}</span>
33
59
  @if (hint()) {
34
60
  <span class="text-label text-(--muted-foreground)">{{ hint() }}</span>
35
61
  }
36
62
  <div class="mt-2 empty:hidden"><ng-content /></div>
37
63
  `,
38
- host: { class: 'flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12' },
39
- }]
40
- }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
41
-
42
- /**
43
- * Generated bundle index. Do not edit.
44
- */
45
-
64
+ host: { class: "flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12" }
65
+ }]
66
+ }],
67
+ propDecorators: {
68
+ heading: [{
69
+ type: i0.Input,
70
+ args: [{
71
+ isSignal: true,
72
+ alias: "heading",
73
+ required: true
74
+ }]
75
+ }],
76
+ hint: [{
77
+ type: i0.Input,
78
+ args: [{
79
+ isSignal: true,
80
+ alias: "hint",
81
+ required: false
82
+ }]
83
+ }]
84
+ }
85
+ });
46
86
  export { UiEmpty };
47
- //# sourceMappingURL=joanroucoux-cairn-ui-empty.mjs.map
87
+
88
+ //# sourceMappingURL=joanroucoux-cairn-ui-empty.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"joanroucoux-cairn-ui-empty.mjs","sources":["../../../projects/ui/empty/empty.ts","../../../projects/ui/empty/joanroucoux-cairn-ui-empty.ts"],"sourcesContent":["import { Component, input } from '@angular/core';\n\n/**\n * Centred empty state: a title, a hint and an action slot, such as a catalogue search that matches\n * nothing. The padding is 24px, 48px from 64rem.\n *\n * @example\n * <ui-empty heading=\"Aucun instrument ne correspond à « zzz »\" hint=\"La recherche porte sur le nom et l'ISIN.\">\n * <button ui-button variant=\"outline\" size=\"md\" type=\"button\">Ajouter une ligne</button>\n * </ui-empty>\n */\n@Component({\n selector: 'ui-empty',\n template: `\n <span class=\"text-body font-medium text-pretty\">{{ heading() }}</span>\n @if (hint()) {\n <span class=\"text-label text-(--muted-foreground)\">{{ hint() }}</span>\n }\n <div class=\"mt-2 empty:hidden\"><ng-content /></div>\n `,\n host: { class: 'flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12' },\n})\nexport class UiEmpty {\n readonly heading = input.required<string>();\n readonly hint = input('');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAEA;;;;;;;;AAQG;MAYU,OAAO,CAAA;IACT,OAAO,GAAG,KAAK,CAAC,QAAQ;gFAAU;IAClC,IAAI,GAAG,KAAK,CAAC,EAAE;6EAAC;uGAFd,OAAO,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAP,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,OAAO,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,mEAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EATR;;;;;;AAMT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAGU,OAAO,EAAA,UAAA,EAAA,CAAA;kBAXnB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,UAAU;AACpB,oBAAA,QAAQ,EAAE;;;;;;AAMT,EAAA,CAAA;AACD,oBAAA,IAAI,EAAE,EAAE,KAAK,EAAE,mEAAmE,EAAE;AACrF,iBAAA;;;ACrBD;;AAEG;;;;"}
1
+ {"version":3,"file":"joanroucoux-cairn-ui-empty.mjs","names":[],"sources":["../../../projects/ui/empty/empty.ts"],"sourcesContent":["import { Component, input } from '@angular/core';\n\n/**\n * Centred empty state: a title, a hint and an action slot, such as a search that matches nothing.\n *\n * @example\n * <ui-empty heading=\"Aucune ligne ne correspond à « zzz »\" hint=\"La recherche porte sur le nom et l'ISIN.\">\n * <button ui-button variant=\"outline\" size=\"md\" type=\"button\">Ajouter une ligne</button>\n * </ui-empty>\n */\n@Component({\n selector: 'ui-empty',\n template: `\n <span class=\"text-body font-medium text-pretty\">{{ heading() }}</span>\n @if (hint()) {\n <span class=\"text-label text-(--muted-foreground)\">{{ hint() }}</span>\n }\n <div class=\"mt-2 empty:hidden\"><ng-content /></div>\n `,\n host: { class: 'flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12' },\n})\nexport class UiEmpty {\n readonly heading = input.required<string>();\n readonly hint = input('');\n}\n"],"mappings":";;AAqBA,IAAa,UAAb,MAAa,QAAO;CACT,UAAU,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAClC,OAAO,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAFd,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAO,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAP,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAO,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA,EAAA,gBAAA,oEAAA;EAAA,UAAA;EAAA,UATR;;;;;;;EAMT,UAAA;CAAA,CAAA;;AAGU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAO,YAAA,CAAA;EAXnB,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,UAAU;;;;;;;GAOV,MAAM,EAAE,OAAO,oEAAmE;EACnF,CAAA"}
@@ -1,16 +1,23 @@
1
- import * as i0 from '@angular/core';
2
- import { Component } from '@angular/core';
3
-
4
- /**
5
- * Link to another site: the label followed by an external-link icon. A 44px target on touch,
6
- * an underline on hover with a mouse.
7
- *
8
- * @example
9
- * <a ui-external-link href="https://example.com" target="_blank" rel="noopener">Page on example.com</a>
10
- */
11
- class UiExternalLink {
12
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiExternalLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
13
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: UiExternalLink, isStandalone: true, selector: "a[ui-external-link]", host: { classAttribute: "inline-flex min-h-11 items-center gap-1.5 rounded-[4px] text-label leading-[17px] font-medium lg:min-h-0 hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) [&>svg]:flex-none" }, ngImport: i0, template: `
1
+ import * as i0 from "@angular/core";
2
+ import { Component } from "@angular/core";
3
+ var UiExternalLink = class UiExternalLink {
4
+ static ɵfac = i0.ɵɵngDeclareFactory({
5
+ minVersion: "12.0.0",
6
+ version: "22.2.0",
7
+ ngImport: i0,
8
+ type: UiExternalLink,
9
+ deps: [],
10
+ target: i0.ɵɵFactoryTarget.Component
11
+ });
12
+ static ɵcmp = i0.ɵɵngDeclareComponent({
13
+ minVersion: "14.0.0",
14
+ version: "22.2.0",
15
+ type: UiExternalLink,
16
+ isStandalone: true,
17
+ selector: "a[ui-external-link]",
18
+ host: { classAttribute: "inline-flex min-h-11 items-center gap-1.5 rounded-[4px] text-label leading-[17px] font-medium lg:min-h-0 hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) [&>svg]:flex-none" },
19
+ ngImport: i0,
20
+ template: `
14
21
  <ng-content />
15
22
  <svg
16
23
  aria-hidden="true"
@@ -27,13 +34,20 @@ class UiExternalLink {
27
34
  <path d="M10 14 21 3" />
28
35
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
29
36
  </svg>
30
- `, isInline: true });
31
- }
32
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiExternalLink, decorators: [{
33
- type: Component,
34
- args: [{
35
- selector: 'a[ui-external-link]',
36
- template: `
37
+ `,
38
+ isInline: true
39
+ });
40
+ };
41
+ i0.ɵɵngDeclareClassMetadata({
42
+ minVersion: "12.0.0",
43
+ version: "22.2.0",
44
+ ngImport: i0,
45
+ type: UiExternalLink,
46
+ decorators: [{
47
+ type: Component,
48
+ args: [{
49
+ selector: "a[ui-external-link]",
50
+ template: `
37
51
  <ng-content />
38
52
  <svg
39
53
  aria-hidden="true"
@@ -51,15 +65,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
51
65
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
52
66
  </svg>
53
67
  `,
54
- host: {
55
- class: 'inline-flex min-h-11 items-center gap-1.5 rounded-[4px] text-label leading-[17px] font-medium lg:min-h-0 hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) [&>svg]:flex-none',
56
- },
57
- }]
58
- }] });
59
-
60
- /**
61
- * Generated bundle index. Do not edit.
62
- */
63
-
68
+ host: { class: "inline-flex min-h-11 items-center gap-1.5 rounded-[4px] text-label leading-[17px] font-medium lg:min-h-0 hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) [&>svg]:flex-none" }
69
+ }]
70
+ }]
71
+ });
64
72
  export { UiExternalLink };
65
- //# sourceMappingURL=joanroucoux-cairn-ui-external-link.mjs.map
73
+
74
+ //# sourceMappingURL=joanroucoux-cairn-ui-external-link.mjs.map