@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-dialog.mjs","sources":["../../../projects/ui/dialog/internal/dialog-exit.ts","../../../projects/ui/dialog/internal/dialog-styles.ts","../../../projects/ui/dialog/internal/sheet-drag.ts","../../../projects/ui/dialog/dialog.ts","../../../projects/ui/dialog/joanroucoux-cairn-ui-dialog.ts"],"sourcesContent":["const GRACE = 50;\n\nexport function afterExit(element: HTMLElement, done: () => void): () => void {\n const style = getComputedStyle(element);\n const wait = longest(style.transitionDuration) + longest(style.transitionDelay);\n\n if (wait === 0) {\n done();\n return () => undefined;\n }\n\n const onEnd = (event: TransitionEvent): void => {\n if (event.target === element && !event.pseudoElement) {\n cancel();\n done();\n }\n };\n const timer = setTimeout(() => {\n cancel();\n done();\n }, wait + GRACE);\n const cancel = (): void => {\n clearTimeout(timer);\n element.removeEventListener('transitionend', onEnd);\n };\n\n element.addEventListener('transitionend', onEnd);\n\n return cancel;\n}\n\nfunction longest(list: string): number {\n return Math.max(0, ...list.split(',').map(toMs));\n}\n\nfunction toMs(value: string): number {\n const amount = Number.parseFloat(value);\n\n if (Number.isNaN(amount)) {\n return 0;\n }\n\n return value.trim().endsWith('ms') ? amount : amount * 1000;\n}\n","export const DIALOG_STYLES = `\ndialog {\n --dialog-ease: var(--ease-sheet);\n transition:\n transform var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n transform: translateY(var(--drag-y, 0px));\n}\n\ndialog:not([open]) {\n transform: translateY(100%);\n transition-duration: var(--duration-exit);\n}\n\n@starting-style {\n dialog[open] {\n transform: translateY(100%);\n }\n}\n\ndialog[data-dragging] {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog {\n transition:\n opacity var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n opacity: 1;\n }\n\n dialog:not([open]) {\n opacity: 0;\n transform: translateY(var(--drag-y, 0px));\n }\n\n @starting-style {\n dialog[open] {\n opacity: 0;\n transform: translateY(0);\n }\n }\n}\n\n@media (min-width: 64rem) {\n dialog {\n --dialog-ease: var(--ease-out);\n transition:\n opacity var(--duration-base) var(--dialog-ease),\n transform var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n opacity: 1;\n transform: scale(1);\n }\n\n dialog:not([open]) {\n opacity: 0;\n transform: scale(var(--enter-scale));\n }\n\n @starting-style {\n dialog[open] {\n opacity: 0;\n transform: scale(var(--enter-scale));\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","const DISMISS_RATIO = 0.3;\nconst DISMISS_SPEED = 0.5;\nconst SPEED_WINDOW = 100;\nconst SLOP = 6;\nconst CONTROLS = 'button, a, input, select, textarea, [role=\"button\"]';\n\ntype Sample = { y: number; at: number };\n\ntype Gesture = {\n id: number;\n startX: number;\n startY: number;\n dragging: boolean;\n height: number;\n offset: number;\n samples: Sample[];\n};\n\nexport class SheetDrag {\n readonly #panel: HTMLElement;\n readonly #grips: readonly HTMLElement[];\n readonly #dismiss: () => void;\n readonly #enabled: () => boolean;\n #gesture: Gesture | null = null;\n\n constructor(panel: HTMLElement, grips: readonly HTMLElement[], dismiss: () => void, enabled: () => boolean) {\n this.#panel = panel;\n this.#grips = grips;\n this.#dismiss = dismiss;\n this.#enabled = enabled;\n\n for (const grip of grips) {\n grip.addEventListener('pointerdown', this.#onDown);\n grip.addEventListener('pointermove', this.#onMove);\n grip.addEventListener('pointerup', this.#onUp);\n grip.addEventListener('pointercancel', this.#onCancel);\n }\n }\n\n reset(): void {\n this.#gesture = null;\n this.#panel.removeAttribute('data-dragging');\n this.#panel.style.removeProperty('--drag-y');\n }\n\n destroy(): void {\n for (const grip of this.#grips) {\n grip.removeEventListener('pointerdown', this.#onDown);\n grip.removeEventListener('pointermove', this.#onMove);\n grip.removeEventListener('pointerup', this.#onUp);\n grip.removeEventListener('pointercancel', this.#onCancel);\n }\n }\n\n readonly #onDown = (event: PointerEvent): void => {\n const grip = event.currentTarget as HTMLElement;\n\n if (event.button !== 0 || !this.#enabled() || !isSheet() || (event.target as Element).closest(CONTROLS)) {\n return;\n }\n\n grip.setPointerCapture(event.pointerId);\n this.#gesture = {\n id: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n dragging: false,\n height: 0,\n offset: 0,\n samples: [{ y: event.clientY, at: event.timeStamp }],\n };\n };\n\n readonly #onMove = (event: PointerEvent): void => {\n const gesture = this.#current(event);\n\n if (!gesture) {\n return;\n }\n\n const dx = event.clientX - gesture.startX;\n const dy = event.clientY - gesture.startY;\n\n if (!gesture.dragging) {\n if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP) {\n return;\n }\n if (Math.abs(dx) > Math.abs(dy)) {\n this.#gesture = null;\n return;\n }\n gesture.dragging = true;\n gesture.height = this.#panel.getBoundingClientRect().height;\n this.#panel.setAttribute('data-dragging', '');\n }\n\n gesture.offset = Math.max(0, dy);\n gesture.samples.push({ y: event.clientY, at: event.timeStamp });\n this.#panel.style.setProperty('--drag-y', `${gesture.offset}px`);\n };\n\n readonly #onUp = (event: PointerEvent): void => {\n const gesture = this.#current(event);\n\n if (!gesture) {\n return;\n }\n\n this.#gesture = null;\n\n if (!gesture.dragging) {\n return;\n }\n\n this.#panel.removeAttribute('data-dragging');\n\n const release = { y: event.clientY, at: event.timeStamp };\n\n if (gesture.offset > gesture.height * DISMISS_RATIO || releaseSpeed(gesture.samples, release) > DISMISS_SPEED) {\n this.#dismiss();\n } else {\n this.reset();\n }\n };\n\n readonly #onCancel = (event: PointerEvent): void => {\n if (this.#current(event)) {\n this.#gesture = null;\n this.#panel.removeAttribute('data-dragging');\n this.reset();\n }\n };\n\n #current(event: PointerEvent): Gesture | null {\n return this.#gesture?.id === event.pointerId ? this.#gesture : null;\n }\n}\n\nfunction isSheet(): boolean {\n return typeof matchMedia === 'function' && !matchMedia('(min-width: 64rem)').matches;\n}\n\nfunction releaseSpeed(samples: readonly Sample[], last: Sample): number {\n const first = samples.find((sample) => sample.at >= last.at - SPEED_WINDOW) ?? last;\n const elapsed = last.at - first.at;\n\n return elapsed > 0 ? (last.y - first.y) / elapsed : 0;\n}\n","import {\n Component,\n DestroyRef,\n ElementRef,\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { afterExit } from './internal/dialog-exit';\nimport { DIALOG_STYLES } from './internal/dialog-styles';\nimport { SheetDrag } from './internal/sheet-drag';\n\n/** Named dialog widths; any other CSS length is accepted as is. `DialogWidth` is derived from this tuple. */\nexport const DIALOG_WIDTHS = ['md', 'lg'] as const;\nexport type DialogWidth = (typeof DIALOG_WIDTHS)[number];\n\nconst WIDTH_VALUES: Record<DialogWidth, string> = {\n md: '28rem',\n lg: '560px',\n};\n\n/** Spacing presets. `DialogLayout` is derived from this tuple. */\nexport const DIALOG_LAYOUTS = ['trade', 'form', 'list', 'confirm'] as const;\nexport type DialogLayout = (typeof DIALOG_LAYOUTS)[number];\n\ntype LayoutClasses = {\n dialog: string;\n header: string;\n title: string;\n cross: string;\n icon: string;\n handle: string;\n body: string;\n footer: string;\n};\n\nconst LAYOUTS: Record<DialogLayout, LayoutClasses> = {\n trade: {\n dialog: '',\n header: 'items-start lg:pt-6',\n title: 'pt-1 lg:pt-0',\n cross: 'lg:-mt-1.5',\n icon: 'lg:size-5',\n handle: 'pt-[7.5px]',\n body: 'pt-3 pb-4 lg:pt-5 lg:pb-5',\n footer: 'max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pb-6',\n },\n form: {\n dialog: '',\n header: 'items-center lg:pt-6',\n title: '',\n cross: '',\n icon: 'lg:size-[22px]',\n handle: 'pt-[7.5px]',\n body: 'pt-2 pb-5 lg:pt-4 lg:pb-5',\n footer: 'max-lg:pb-[calc(12px+env(safe-area-inset-bottom))] lg:pb-5',\n },\n list: {\n dialog: 'max-lg:h-[calc(100dvh-58px)] lg:mt-24 lg:mb-auto lg:max-h-[min(760px,calc(100dvh-96px-32px))]',\n header: 'items-center lg:pt-5',\n title: '',\n cross: '',\n icon: 'lg:size-[22px]',\n handle: 'pt-[7.5px]',\n body: 'pt-2 pb-4 lg:pt-4 lg:pb-5',\n footer: 'pt-3 shadow-[0_-1px_0_var(--hairline)] max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pt-4 lg:pb-4',\n },\n confirm: {\n dialog: '',\n header: 'items-center pt-3 lg:pt-6',\n title: '',\n cross: '',\n icon: 'lg:size-[22px]',\n handle: 'pt-[11.5px]',\n body: 'pt-3 pb-5 lg:pt-3 lg:pb-6',\n footer: 'max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pb-6',\n },\n};\n\n/** What started a close, as `closed` reports it. `DialogCloseReason` is derived from this tuple. */\nexport const DIALOG_CLOSE_REASONS = ['escape', 'backdrop', 'cross', 'drag', 'programmatic'] as const;\nexport type DialogCloseReason = (typeof DIALOG_CLOSE_REASONS)[number];\n\nconst nextId = (() => {\n let count = 0;\n\n return () => `ui-dialog-${++count}`;\n})();\n\n/**\n * Modal dialog on the native <dialog>: a bottom sheet under `64rem`, centered above it.\n *\n * `busy` keeps it open while its action runs: Escape, a click on the backdrop and a drag of the sheet do\n * nothing and the cross is disabled. The owner disables its own buttons and still closes it through `open`.\n *\n * @example\n * <ui-dialog heading=\"Buy\" description=\"Ferrari\" closeLabel=\"Close\" width=\"528px\" [open]=\"buying()\" (dismissed)=\"buying.set(false)\">\n * <p>Body</p>\n * <button dialogActions ui-button variant=\"outline\" class=\"max-lg:hidden\" (click)=\"buying.set(false)\">Cancel</button>\n * <button dialogActions ui-button>Buy 40 shares</button>\n * </ui-dialog>\n */\n@Component({\n selector: 'ui-dialog',\n host: {\n '(pointerdown)': 'onPointerDown($event)',\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeydown($event)',\n },\n template: `\n <dialog\n #dlg\n [attr.aria-busy]=\"busy() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-labelledby]=\"headingId\"\n [attr.role]=\"layout() === 'confirm' ? 'alertdialog' : 'dialog'\"\n [class]=\"classes()\"\n [style.--dialog-width]=\"widthValue()\"\n (cancel)=\"onCancel($event)\"\n (close)=\"onNativeClose()\"\n >\n <div\n aria-hidden=\"true\"\n class=\"flex h-5 touch-none justify-center lg:hidden\"\n data-dialog-handle\n [class]=\"spec().handle\"\n >\n <span class=\"rounded-pill h-[5px] w-9 bg-(--border)\"></span>\n </div>\n\n <div data-dialog-header [class]=\"headerClasses()\">\n <div class=\"flex min-w-0 flex-1 flex-col\" [class]=\"titleClasses()\">\n <h2 class=\"text-title font-semibold\" [id]=\"headingId\">{{ heading() }}</h2>\n\n @if (description()) {\n <p\n class=\"text-label text-(--muted-foreground)\"\n [class.truncate]=\"truncateDescription()\"\n [id]=\"descriptionId\"\n >\n {{ description() }}\n </p>\n }\n </div>\n\n @if (closeLabel()) {\n <button\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n [class]=\"crossClasses()\"\n [disabled]=\"busy()\"\n (click)=\"close('cross')\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"block size-[22px] fill-none stroke-current stroke-[1.75]\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n viewBox=\"0 0 24 24\"\n [class]=\"iconClasses()\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n </div>\n\n <div\n class=\"min-h-0 flex-1 overflow-y-auto px-4 outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) lg:px-6\"\n data-dialog-body\n [attr.aria-labelledby]=\"scrollable() ? headingId : null\"\n [attr.role]=\"scrollable() ? 'region' : null\"\n [attr.tabindex]=\"scrollable() ? 0 : null\"\n [id]=\"bodyId\"\n >\n <!-- empty:hidden keeps a body-less dialog from carrying the body's vertical padding. -->\n <div class=\"flex flex-col gap-4 empty:hidden\" [class]=\"spec().body\">\n <ng-content />\n </div>\n </div>\n\n <div data-dialog-footer [class]=\"footerClasses()\">\n <ng-content select=\"[dialogActions]\" />\n </div>\n </dialog>\n `,\n styles: DIALOG_STYLES,\n})\nexport class UiDialog {\n readonly heading = input.required<string>();\n readonly description = input<string>();\n readonly closeLabel = input<string>();\n readonly layout = input<DialogLayout>('trade');\n readonly truncateDescription = input(false, { transform: booleanAttribute });\n readonly width = input<DialogWidth | (string & {})>('lg');\n readonly open = input(false, { transform: booleanAttribute });\n readonly busy = input(false, { transform: booleanAttribute });\n readonly dismissed = output<void>();\n readonly closed = output<DialogCloseReason>();\n\n protected readonly headingId = nextId();\n protected readonly descriptionId = `${this.headingId}-description`;\n protected readonly bodyId = `${this.headingId}-body`;\n protected readonly scrollable = signal(false);\n\n protected readonly describedBy = computed(() => {\n if (this.description()) {\n return this.descriptionId;\n }\n\n return this.layout() === 'confirm' ? this.bodyId : null;\n });\n\n protected readonly widthValue = computed(() => {\n const width = this.width();\n\n return Object.hasOwn(WIDTH_VALUES, width) ? WIDTH_VALUES[width as DialogWidth] : width;\n });\n\n protected readonly spec = computed(() => LAYOUTS[this.layout()]);\n\n protected readonly headerClasses = computed(\n () =>\n `flex gap-2 pl-4 max-lg:touch-none lg:gap-3 lg:pl-6 ${this.spec().header} ${this.closeLabel() ? 'pr-2 lg:pr-4' : 'pr-4 lg:pr-6'}`,\n );\n\n protected readonly titleClasses = computed(() => this.spec().title);\n\n protected readonly crossClasses = computed(\n () =>\n `rounded-pill lg:rounded-control grid size-11 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out outline-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-(--ring) active:scale-(--press-scale) active:bg-(--soft) disabled:pointer-events-none disabled:opacity-40 lg:size-9 lg:hover:bg-(--glow) lg:hover:text-(--foreground) ${this.spec().cross}`,\n );\n\n protected readonly iconClasses = computed(() => this.spec().icon);\n\n protected readonly footerClasses = computed(\n () =>\n `max-lg:[&>[ui-button]]:text-body flex justify-end gap-2 px-4 empty:hidden max-lg:flex-col-reverse max-lg:[&>[ui-button]]:h-[50px] max-lg:[&>[ui-button]]:w-full lg:px-6 ${this.spec().footer}`,\n );\n\n protected readonly classes = computed(\n () =>\n `open:flex w-full flex-col overflow-hidden m-auto mt-auto max-h-[calc(100dvh-2rem)] max-lg:mb-0 max-lg:max-w-none max-lg:rounded-b-none lg:max-w-(--dialog-width) rounded-container bg-(--card) text-(--foreground) shadow-[0_0_0_1px_var(--border),0_24px_64px_rgb(0_0_0/0.24)] max-lg:shadow-[0_-1px_0_var(--border),0_-12px_32px_rgb(0_0_0/0.16)] backdrop:bg-black/[0.36] ${this.spec().dialog}`,\n );\n\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n\n afterNextRender(() => {\n const host = this.#host.nativeElement;\n const grips = ['[data-dialog-handle]', '[data-dialog-header]'].map(\n (grip) => host.querySelector(grip) as HTMLElement,\n );\n\n this.#drag = new SheetDrag(\n this.#dialog,\n grips,\n () => this.close('drag'),\n () => !this.busy(),\n );\n destroyRef.onDestroy(() => {\n this.#drag?.destroy();\n this.#cancelExit();\n });\n\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n\n const body = this.#host.nativeElement.querySelector('[data-dialog-body]') as HTMLElement;\n const observer = new ResizeObserver(() => this.scrollable.set(body.scrollHeight > body.clientHeight));\n\n observer.observe(body);\n observer.observe(body.firstElementChild as Element);\n destroyRef.onDestroy(() => observer.disconnect());\n });\n\n afterRenderEffect(() => {\n // viewChild.required can't target a #private field (NG1053); safe because the template has exactly one <dialog>.\n const dialog = this.#host.nativeElement.querySelector('dialog') as HTMLDialogElement;\n\n if (this.open() && !dialog.open) {\n this.#cancelExit();\n this.#drag?.reset();\n dialog.showModal();\n } else if (!this.open() && dialog.open) {\n this.close('programmatic');\n }\n });\n }\n\n #pressedOnBackdrop = false;\n #drag: SheetDrag | null = null;\n #reason: DialogCloseReason | null = null;\n #cancelExit: () => void = () => undefined;\n\n protected close(reason: DialogCloseReason): void {\n if (this.#dialog.open) {\n this.#reason = reason;\n this.#dialog.close();\n }\n }\n\n protected onCancel(event: Event): void {\n if (event.target !== event.currentTarget) {\n return;\n }\n event.preventDefault();\n if (!this.busy()) {\n this.close('escape');\n }\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.busy()) {\n event.preventDefault();\n }\n }\n\n protected onPointerDown(event: MouseEvent): void {\n this.#pressedOnBackdrop = event.target === this.#dialog && this.#outside(event);\n }\n\n protected onClick(event: MouseEvent): void {\n if (this.#pressedOnBackdrop && !this.busy() && event.target === this.#dialog && this.#outside(event)) {\n this.close('backdrop');\n }\n this.#pressedOnBackdrop = false;\n }\n\n get #dialog(): HTMLDialogElement {\n return this.#host.nativeElement.querySelector('dialog') as HTMLDialogElement;\n }\n\n #outside(event: MouseEvent): boolean {\n const rect = this.#dialog.getBoundingClientRect();\n\n return (\n event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom\n );\n }\n\n protected onNativeClose(): void {\n const asked = this.#reason;\n\n this.#reason = null;\n if (this.#dialog.open) {\n return;\n }\n if (asked === null && this.busy()) {\n this.#dialog.showModal();\n return;\n }\n const reason = asked ?? 'escape';\n if (reason !== 'programmatic') {\n this.dismissed.emit();\n }\n this.#awaitExit(reason);\n }\n\n #awaitExit(reason: DialogCloseReason): void {\n this.#cancelExit();\n this.#cancelExit = afterExit(this.#dialog, () => {\n this.#cancelExit = () => undefined;\n this.#drag?.reset();\n this.closed.emit(reason);\n });\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-dialog.mjs","names":[],"sources":["../../../projects/ui/dialog/internal/dialog-styles.ts","../../../projects/ui/dialog/internal/dialog-exit.ts","../../../projects/ui/dialog/internal/modal-close.ts","../../../projects/ui/dialog/internal/sheet-drag.ts","../../../projects/ui/dialog/dialog.ts"],"sourcesContent":["export const DIALOG_STYLES = `\ndialog {\n --dialog-ease: var(--ease-sheet);\n transition:\n transform var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n transform: translateY(var(--drag-y, 0px));\n}\n\ndialog:not([open]) {\n transform: translateY(100%);\n transition-duration: var(--duration-exit);\n}\n\n@starting-style {\n dialog[open] {\n transform: translateY(100%);\n }\n}\n\ndialog[data-dragging] {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog {\n transition:\n opacity var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n opacity: 1;\n }\n\n dialog:not([open]) {\n opacity: 0;\n transform: translateY(var(--drag-y, 0px));\n }\n\n @starting-style {\n dialog[open] {\n opacity: 0;\n transform: translateY(0);\n }\n }\n}\n\n@media (min-width: 64rem) {\n dialog {\n --dialog-ease: var(--ease-out);\n transition:\n opacity var(--duration-base) var(--dialog-ease),\n transform var(--duration-base) var(--dialog-ease),\n overlay var(--duration-base) allow-discrete,\n display var(--duration-base) allow-discrete;\n opacity: 1;\n transform: scale(1);\n }\n\n dialog:not([open]) {\n opacity: 0;\n transform: scale(var(--enter-scale));\n }\n\n @starting-style {\n dialog[open] {\n opacity: 0;\n transform: scale(var(--enter-scale));\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","const GRACE = 50;\n\nexport function afterExit(element: HTMLElement, done: () => void): () => void {\n const style = getComputedStyle(element);\n const wait = longest(style.transitionDuration) + longest(style.transitionDelay);\n\n if (wait === 0) {\n done();\n return () => undefined;\n }\n\n const onEnd = (event: TransitionEvent): void => {\n if (event.target === element && !event.pseudoElement) {\n cancel();\n done();\n }\n };\n const timer = setTimeout(() => {\n cancel();\n done();\n }, wait + GRACE);\n const cancel = (): void => {\n clearTimeout(timer);\n element.removeEventListener('transitionend', onEnd);\n };\n\n element.addEventListener('transitionend', onEnd);\n\n return cancel;\n}\n\nfunction longest(list: string): number {\n return Math.max(0, ...list.split(',').map(toMs));\n}\n\nfunction toMs(value: string): number {\n const amount = Number.parseFloat(value);\n\n if (Number.isNaN(amount)) {\n return 0;\n }\n\n return value.trim().endsWith('ms') ? amount : amount * 1000;\n}\n","import { DestroyRef, ElementRef, type OutputEmitterRef, type Signal, afterRenderEffect, inject } from '@angular/core';\n\nimport { afterExit } from './dialog-exit';\n\ntype SharedReason = 'escape' | 'backdrop' | 'cross' | 'programmatic';\n\nexport type ModalCloseConfig<R extends string> = {\n readonly open: Signal<boolean>;\n readonly busy: Signal<boolean>;\n readonly dismissed: OutputEmitterRef<void>;\n readonly closed: OutputEmitterRef<R | SharedReason>;\n readonly reset?: () => void;\n readonly opening?: () => void;\n};\n\n/**\n * The close protocol of a modal on the native <dialog> under the host: `open` drives `showModal()` and `close()`,\n * Escape and a press-and-release on the veil close it unless `busy`, `dismissed` reports a close the user asked for\n * and `closed` its reason once the exit transition has played. `opening` runs just before `showModal()`. Must be\n * created in an injection context.\n */\nexport class ModalClose<R extends string> {\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #config: ModalCloseConfig<R>;\n\n #pressedOnBackdrop = false;\n #reason: R | SharedReason | null = null;\n #cancelExit: () => void = () => undefined;\n\n constructor(config: ModalCloseConfig<R>) {\n this.#config = config;\n inject(DestroyRef).onDestroy(() => this.#cancelExit());\n\n afterRenderEffect(() => {\n const dialog = this.#dialog;\n\n if (config.open() && !dialog.open) {\n this.#cancelExit();\n config.reset?.();\n config.opening?.();\n dialog.showModal();\n } else if (!config.open() && dialog.open) {\n this.close('programmatic');\n }\n });\n }\n\n close(reason: R | SharedReason): void {\n if (this.#dialog.open) {\n this.#reason = reason;\n this.#dialog.close();\n }\n }\n\n onCancel(event: Event): void {\n if (event.target !== event.currentTarget) {\n return;\n }\n event.preventDefault();\n if (!this.#config.busy()) {\n this.close('escape');\n }\n }\n\n onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.#config.busy() && (event.target as Element).closest('dialog') === this.#dialog) {\n event.preventDefault();\n }\n }\n\n onPointerDown(event: MouseEvent): void {\n this.#pressedOnBackdrop = event.target === this.#dialog && this.#outside(event);\n }\n\n onClick(event: MouseEvent): void {\n if (this.#pressedOnBackdrop && !this.#config.busy() && event.target === this.#dialog && this.#outside(event)) {\n this.close('backdrop');\n }\n this.#pressedOnBackdrop = false;\n }\n\n onNativeClose(): void {\n const asked = this.#reason;\n\n this.#reason = null;\n if (this.#dialog.open) {\n return;\n }\n if (asked === null && this.#config.busy()) {\n this.#dialog.showModal();\n return;\n }\n const reason = asked ?? 'escape';\n if (reason !== 'programmatic') {\n this.#config.dismissed.emit();\n }\n this.#cancelExit();\n this.#cancelExit = afterExit(this.#dialog, () => {\n this.#cancelExit = () => undefined;\n this.#config.reset?.();\n this.#config.closed.emit(reason);\n });\n }\n\n get #dialog(): HTMLDialogElement {\n return this.#host.nativeElement.querySelector('dialog') as HTMLDialogElement;\n }\n\n #outside(event: MouseEvent): boolean {\n const rect = this.#dialog.getBoundingClientRect();\n\n return (\n event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom\n );\n }\n}\n","const DISMISS_RATIO = 0.3;\nconst DISMISS_SPEED = 0.5;\nconst SPEED_WINDOW = 100;\nconst SLOP = 6;\nconst CONTROLS = 'button, a, input, select, textarea, [role=\"button\"]';\n\ntype Sample = { y: number; at: number };\n\ntype Gesture = {\n id: number;\n startX: number;\n startY: number;\n dragging: boolean;\n height: number;\n offset: number;\n samples: Sample[];\n};\n\nexport class SheetDrag {\n readonly #panel: HTMLElement;\n readonly #grips: readonly HTMLElement[];\n readonly #dismiss: () => void;\n readonly #enabled: () => boolean;\n #gesture: Gesture | null = null;\n\n constructor(panel: HTMLElement, grips: readonly HTMLElement[], dismiss: () => void, enabled: () => boolean) {\n this.#panel = panel;\n this.#grips = grips;\n this.#dismiss = dismiss;\n this.#enabled = enabled;\n\n for (const grip of grips) {\n grip.addEventListener('pointerdown', this.#onDown);\n grip.addEventListener('pointermove', this.#onMove);\n grip.addEventListener('pointerup', this.#onUp);\n grip.addEventListener('pointercancel', this.#onCancel);\n }\n }\n\n reset(): void {\n this.#gesture = null;\n this.#panel.removeAttribute('data-dragging');\n this.#panel.style.removeProperty('--drag-y');\n }\n\n destroy(): void {\n for (const grip of this.#grips) {\n grip.removeEventListener('pointerdown', this.#onDown);\n grip.removeEventListener('pointermove', this.#onMove);\n grip.removeEventListener('pointerup', this.#onUp);\n grip.removeEventListener('pointercancel', this.#onCancel);\n }\n }\n\n readonly #onDown = (event: PointerEvent): void => {\n const grip = event.currentTarget as HTMLElement;\n\n if (event.button !== 0 || !this.#enabled() || !isSheet() || (event.target as Element).closest(CONTROLS)) {\n return;\n }\n\n grip.setPointerCapture(event.pointerId);\n this.#gesture = {\n id: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n dragging: false,\n height: 0,\n offset: 0,\n samples: [{ y: event.clientY, at: event.timeStamp }],\n };\n };\n\n readonly #onMove = (event: PointerEvent): void => {\n const gesture = this.#current(event);\n\n if (!gesture) {\n return;\n }\n\n const dx = event.clientX - gesture.startX;\n const dy = event.clientY - gesture.startY;\n\n if (!gesture.dragging) {\n if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP) {\n return;\n }\n if (Math.abs(dx) > Math.abs(dy)) {\n this.#gesture = null;\n return;\n }\n gesture.dragging = true;\n gesture.height = this.#panel.getBoundingClientRect().height;\n this.#panel.setAttribute('data-dragging', '');\n }\n\n gesture.offset = Math.max(0, dy);\n gesture.samples.push({ y: event.clientY, at: event.timeStamp });\n this.#panel.style.setProperty('--drag-y', `${gesture.offset}px`);\n };\n\n readonly #onUp = (event: PointerEvent): void => {\n const gesture = this.#current(event);\n\n if (!gesture) {\n return;\n }\n\n this.#gesture = null;\n\n if (!gesture.dragging) {\n return;\n }\n\n this.#panel.removeAttribute('data-dragging');\n\n const release = { y: event.clientY, at: event.timeStamp };\n\n if (gesture.offset > gesture.height * DISMISS_RATIO || releaseSpeed(gesture.samples, release) > DISMISS_SPEED) {\n this.#dismiss();\n } else {\n this.reset();\n }\n };\n\n readonly #onCancel = (event: PointerEvent): void => {\n if (this.#current(event)) {\n this.#gesture = null;\n this.#panel.removeAttribute('data-dragging');\n this.reset();\n }\n };\n\n #current(event: PointerEvent): Gesture | null {\n return this.#gesture?.id === event.pointerId ? this.#gesture : null;\n }\n}\n\nfunction isSheet(): boolean {\n return typeof matchMedia === 'function' && !matchMedia('(min-width: 64rem)').matches;\n}\n\nfunction releaseSpeed(samples: readonly Sample[], last: Sample): number {\n const first = samples.find((sample) => sample.at >= last.at - SPEED_WINDOW) ?? last;\n const elapsed = last.at - first.at;\n\n return elapsed > 0 ? (last.y - first.y) / elapsed : 0;\n}\n","import {\n Component,\n DestroyRef,\n ElementRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { DIALOG_STYLES } from './internal/dialog-styles';\nimport { ModalClose } from './internal/modal-close';\nimport { SheetDrag } from './internal/sheet-drag';\n\n/** Named dialog widths; any other CSS length is accepted as is. */\nexport const DIALOG_WIDTHS = ['md', 'lg'] as const;\nexport type DialogWidth = (typeof DIALOG_WIDTHS)[number];\n\nconst WIDTH_VALUES: Record<DialogWidth, string> = {\n md: '28rem',\n lg: '560px',\n};\n\n/** `confirm` is a short alertdialog: no cross, no hairlines, one 24px padding, 440px wide unless told otherwise. */\nexport const DIALOG_VARIANTS = ['default', 'confirm'] as const;\nexport type DialogVariant = (typeof DIALOG_VARIANTS)[number];\n\n/** `full` makes the sheet under `64rem` fill the screen below the status area; `fit` sizes it to its content. */\nexport const DIALOG_SHEETS = ['fit', 'full'] as const;\nexport type DialogSheet = (typeof DIALOG_SHEETS)[number];\n\ntype VariantClasses = {\n handle: string;\n header: string;\n body: string;\n footer: string;\n};\n\nconst VARIANTS: Record<DialogVariant, VariantClasses> = {\n default: {\n handle: 'pt-[7.5px]',\n header: 'pb-3 lg:py-4 lg:pl-6',\n body: 'py-4 lg:py-5',\n footer: 'pt-3 empty:hidden lg:py-4',\n },\n confirm: {\n handle: 'pt-[11.5px]',\n header: 'pt-3 lg:pt-6 lg:pl-6',\n body: 'pt-3',\n footer: 'not-empty:pt-5 lg:pb-6 lg:not-empty:pt-6',\n },\n};\n\nconst DEFAULT_WIDTHS: Record<DialogVariant, string> = {\n default: 'lg',\n confirm: '440px',\n};\n\n/** What started a close, as `closed` reports it. */\nexport const DIALOG_CLOSE_REASONS = ['escape', 'backdrop', 'cross', 'drag', 'programmatic'] as const;\nexport type DialogCloseReason = (typeof DIALOG_CLOSE_REASONS)[number];\n\nconst nextId = (() => {\n let count = 0;\n\n return () => `ui-dialog-${++count}`;\n})();\n\n/**\n * Modal dialog on the native <dialog>: a bottom sheet under `64rem`, centered above it.\n *\n * `busy` keeps it open while its action runs: Escape, a backdrop click and a sheet drag do nothing\n * and the cross is disabled. The owner disables its own buttons and still closes it through `open`.\n *\n * @example\n * <ui-dialog heading=\"Buy\" description=\"Ferrari\" closeLabel=\"Close\" width=\"528px\" [open]=\"buying()\" (dismissed)=\"buying.set(false)\">\n * <p>Body</p>\n * <button dialogActions ui-button variant=\"outline\" class=\"max-lg:hidden\" (click)=\"buying.set(false)\">Cancel</button>\n * <button dialogActions ui-button>Buy 40 shares</button>\n * </ui-dialog>\n */\n@Component({\n selector: 'ui-dialog',\n host: {\n '(pointerdown)': 'modal.onPointerDown($event)',\n '(click)': 'modal.onClick($event)',\n '(keydown)': 'modal.onKeydown($event)',\n },\n template: `\n <dialog\n #dlg\n [attr.aria-busy]=\"busy() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-labelledby]=\"headingId\"\n [attr.role]=\"variant() === 'confirm' ? 'alertdialog' : 'dialog'\"\n [class]=\"classes()\"\n [style.--dialog-width]=\"widthValue()\"\n (cancel)=\"modal.onCancel($event)\"\n (close)=\"modal.onNativeClose()\"\n >\n <div\n aria-hidden=\"true\"\n class=\"flex h-5 touch-none justify-center lg:hidden\"\n data-dialog-handle\n [class]=\"spec().handle\"\n >\n <span class=\"rounded-pill h-[5px] w-9 bg-(--border)\"></span>\n </div>\n\n <div data-dialog-header [class]=\"headerClasses()\">\n <div class=\"flex min-w-0 flex-1 flex-col\" [class.max-lg:pt-1]=\"description()\">\n <h2 class=\"text-title font-semibold\" [id]=\"headingId\">{{ heading() }}</h2>\n\n @if (description()) {\n <p\n class=\"text-label text-(--muted-foreground)\"\n [class.truncate]=\"truncateDescription()\"\n [id]=\"descriptionId\"\n >\n {{ description() }}\n </p>\n }\n </div>\n\n @if (closeLabel()) {\n <button\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n [class]=\"crossClasses\"\n [disabled]=\"busy()\"\n (click)=\"modal.close('cross')\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"block size-[22px] 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 <div\n class=\"min-h-0 flex-1 overflow-y-auto px-4 outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) lg:px-6\"\n data-dialog-body\n [attr.aria-labelledby]=\"scrollable() ? headingId : null\"\n [attr.role]=\"scrollable() ? 'region' : null\"\n [attr.tabindex]=\"scrollable() ? 0 : null\"\n [id]=\"bodyId\"\n >\n <div class=\"flex flex-col gap-4 empty:hidden\" [class]=\"spec().body\">\n <ng-content />\n </div>\n </div>\n\n <div data-dialog-footer [class]=\"footerClasses()\">\n <ng-content select=\"[dialogActions]\" />\n </div>\n </dialog>\n `,\n styles: DIALOG_STYLES,\n})\nexport class UiDialog {\n readonly heading = input.required<string>();\n readonly description = input<string>();\n readonly closeLabel = input<string>();\n readonly variant = input<DialogVariant>('default');\n readonly sheet = input<DialogSheet>('fit');\n readonly truncateDescription = input(false, { transform: booleanAttribute });\n readonly width = input<DialogWidth | (string & {})>();\n readonly open = input(false, { transform: booleanAttribute });\n readonly busy = input(false, { transform: booleanAttribute });\n readonly dismissed = output<void>();\n readonly closed = output<DialogCloseReason>();\n\n protected readonly headingId = nextId();\n protected readonly descriptionId = `${this.headingId}-description`;\n protected readonly bodyId = `${this.headingId}-body`;\n protected readonly scrollable = signal(false);\n protected readonly stacked = signal(false);\n\n protected readonly describedBy = computed(() => {\n if (this.description()) {\n return this.descriptionId;\n }\n\n return this.variant() === 'confirm' ? this.bodyId : null;\n });\n\n protected readonly widthValue = computed(() => {\n const width = this.width() ?? DEFAULT_WIDTHS[this.variant()];\n\n return Object.hasOwn(WIDTH_VALUES, width) ? WIDTH_VALUES[width as DialogWidth] : width;\n });\n\n protected readonly spec = computed(() => VARIANTS[this.variant()]);\n\n protected readonly hairlines = computed(() => Boolean(this.closeLabel()) && this.variant() === 'default');\n\n protected readonly headerClasses = computed(\n () =>\n `flex items-start gap-2 pl-4 max-lg:touch-none lg:gap-3 ${this.spec().header} ${this.closeLabel() ? 'pr-2 lg:pr-4' : 'pr-4 lg:pr-6'} ${this.hairlines() ? 'shadow-[inset_0_-1px_0_var(--hairline)]' : ''}`,\n );\n\n protected readonly crossClasses =\n 'rounded-pill lg:rounded-control grid size-11 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out outline-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-(--ring) active:scale-(--press-scale) active:bg-(--soft) disabled:pointer-events-none disabled:opacity-40 lg:size-9 lg:hover:bg-(--glow) lg:hover:text-(--foreground)';\n\n protected readonly footerClasses = computed(\n () =>\n `max-lg:[&>[ui-button]]:text-body flex justify-end gap-2 px-4 pb-[calc(8px+env(safe-area-inset-bottom))] max-lg:flex-col-reverse max-lg:[&>[ui-button]]:h-[50px] max-lg:[&>[ui-button]]:w-full lg:px-6 ${this.spec().footer} ${this.hairlines() ? 'shadow-[inset_0_1px_0_var(--hairline)]' : ''}`,\n );\n\n protected readonly classes = computed(\n () =>\n `open:flex w-full flex-col overflow-hidden m-auto mt-auto max-lg:max-h-[calc(100dvh-2rem)] lg:max-h-[calc(100dvh-96px)] max-lg:mb-0 max-lg:max-w-none max-lg:rounded-b-none lg:max-w-(--dialog-width) rounded-container bg-(--card) text-(--foreground) shadow-[0_0_0_1px_var(--border),0_24px_64px_rgb(0_0_0/0.24)] max-lg:shadow-[0_-1px_0_var(--border),0_-12px_32px_rgb(0_0_0/0.16)] ${this.stacked() ? 'backdrop:bg-black/[0.24]' : 'backdrop:bg-black/[0.36]'} ${this.sheet() === 'full' ? 'max-lg:h-[calc(100dvh-58px)]' : ''}`,\n );\n\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n #drag: SheetDrag | null = null;\n\n protected readonly modal: ModalClose<DialogCloseReason>;\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n\n afterNextRender(() => {\n const host = this.#host.nativeElement;\n const dialog = host.querySelector('dialog') as HTMLDialogElement;\n const grips = ['[data-dialog-handle]', '[data-dialog-header]'].map(\n (grip) => host.querySelector(grip) as HTMLElement,\n );\n\n this.#drag = new SheetDrag(\n dialog,\n grips,\n () => this.modal.close('drag'),\n () => !this.busy(),\n );\n destroyRef.onDestroy(() => this.#drag?.destroy());\n\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n\n const body = host.querySelector('[data-dialog-body]') as HTMLElement;\n const observer = new ResizeObserver(() => this.scrollable.set(body.scrollHeight > body.clientHeight));\n\n observer.observe(body);\n observer.observe(body.firstElementChild as Element);\n destroyRef.onDestroy(() => observer.disconnect());\n });\n\n this.modal = new ModalClose<DialogCloseReason>({\n open: this.open,\n busy: this.busy,\n dismissed: this.dismissed,\n closed: this.closed,\n reset: () => this.#drag?.reset(),\n opening: () => this.stacked.set(this.#overAnotherModal()),\n });\n }\n\n #overAnotherModal(): boolean {\n const open = this.#host.nativeElement.ownerDocument.querySelectorAll('dialog[open]');\n\n return Array.from(open).some((dialog) => dialog.matches(':modal'));\n }\n}\n"],"mappings":"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"}
@@ -1,153 +1,185 @@
1
- import { NgTemplateOutlet } from '@angular/common';
2
- import * as i0 from '@angular/core';
3
- import { input, output, signal, computed, Component } from '@angular/core';
4
-
5
- const RADIUS = 78;
6
- const RING_WIDTH = 30;
7
- const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import * as i0 from "@angular/core";
3
+ import { Component, computed, input, output, signal } from "@angular/core";
4
+ const CIRCUMFERENCE = 2 * Math.PI * 78;
8
5
  const GAP = 2;
9
6
  function positionSlices(slices) {
10
- let travelled = 0;
11
- return slices.map((slice) => {
12
- const length = slice.share * CIRCUMFERENCE;
13
- const drawn = Math.max(0, length - GAP);
14
- const positioned = {
15
- ...slice,
16
- dash: `${drawn.toFixed(2)} ${(CIRCUMFERENCE - drawn).toFixed(2)}`,
17
- offset: (-travelled).toFixed(2),
18
- };
19
- travelled += length;
20
- return positioned;
21
- });
7
+ let travelled = 0;
8
+ return slices.map((slice) => {
9
+ const length = slice.share * CIRCUMFERENCE;
10
+ const drawn = Math.max(0, length - GAP);
11
+ const positioned = {
12
+ ...slice,
13
+ dash: `${drawn.toFixed(2)} ${(CIRCUMFERENCE - drawn).toFixed(2)}`,
14
+ offset: (-travelled).toFixed(2)
15
+ };
16
+ travelled += length;
17
+ return positioned;
18
+ });
22
19
  }
23
-
24
20
  const MAX_RAMP = 6;
25
- const OTHERS_ID = 'others';
21
+ const OTHERS_ID = "others";
26
22
  function shareSlices(slices) {
27
- const positive = slices.filter((slice) => slice.value > 0).sort((a, b) => b.value - a.value);
28
- const total = positive.reduce((sum, slice) => sum + slice.value, 0);
29
- return positive.map((slice) => ({ ...slice, share: slice.value / total }));
23
+ const positive = slices.filter((slice) => slice.value > 0).sort((a, b) => b.value - a.value);
24
+ const total = positive.reduce((sum, slice) => sum + slice.value, 0);
25
+ return positive.map((slice) => ({
26
+ ...slice,
27
+ share: slice.value / total
28
+ }));
30
29
  }
31
30
  function rankSlices(slices, othersLabel) {
32
- const shared = shareSlices(slices);
33
- const toRanked = (slice, ramp, members = []) => ({
34
- ...slice,
35
- ramp,
36
- members,
37
- });
38
- if (shared.length <= MAX_RAMP) {
39
- return shared.map((slice, index) => toRanked(slice, (index + 1)));
40
- }
41
- const kept = shared.slice(0, MAX_RAMP - 1);
42
- const rest = shared.slice(MAX_RAMP - 1);
43
- const othersValue = rest.reduce((sum, slice) => sum + slice.value, 0);
44
- const othersShare = rest.reduce((sum, slice) => sum + slice.share, 0);
45
- return [
46
- ...kept.map((slice, index) => toRanked(slice, (index + 1))),
47
- toRanked({ id: OTHERS_ID, label: othersLabel, value: othersValue, share: othersShare }, MAX_RAMP, rest.map((slice) => slice.label)),
48
- ];
31
+ const shared = shareSlices(slices);
32
+ const toRanked = (slice, ramp, members = []) => ({
33
+ ...slice,
34
+ ramp,
35
+ members
36
+ });
37
+ if (shared.length <= MAX_RAMP) return shared.map((slice, index) => toRanked(slice, index + 1));
38
+ const kept = shared.slice(0, 5);
39
+ const rest = shared.slice(5);
40
+ const othersValue = rest.reduce((sum, slice) => sum + slice.value, 0);
41
+ const othersShare = rest.reduce((sum, slice) => sum + slice.share, 0);
42
+ return [...kept.map((slice, index) => toRanked(slice, index + 1)), toRanked({
43
+ id: OTHERS_ID,
44
+ label: othersLabel,
45
+ value: othersValue,
46
+ share: othersShare
47
+ }, MAX_RAMP, rest.map((slice) => slice.label))];
49
48
  }
50
-
51
49
  const identityValue = (value) => `${value}`;
52
50
  const identityShare = (share) => `${Math.round(share * 100)}%`;
53
- const ROW_CLASSES = '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)';
54
- const SLICE_CLASSES = 'cursor-pointer origin-center [transform-box:view-box] transition-[scale,opacity] duration-(--duration-fast) ease-out';
51
+ const ROW_CLASSES = "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)";
52
+ const SLICE_CLASSES = "cursor-pointer origin-center [transform-box:view-box] transition-[scale,opacity] duration-(--duration-fast) ease-out";
55
53
  const RAMP_DOT_CLASSES = {
56
- 1: 'bg-(--ramp-1)',
57
- 2: 'bg-(--ramp-2)',
58
- 3: 'bg-(--ramp-3)',
59
- 4: 'bg-(--ramp-4)',
60
- 5: 'bg-(--ramp-5)',
61
- 6: 'bg-(--ramp-6)',
54
+ 1: "bg-(--ramp-1)",
55
+ 2: "bg-(--ramp-2)",
56
+ 3: "bg-(--ramp-3)",
57
+ 4: "bg-(--ramp-4)",
58
+ 5: "bg-(--ramp-5)",
59
+ 6: "bg-(--ramp-6)"
62
60
  };
63
- /**
64
- * A ring chart with a legend: slices rank from largest to smallest on a grey ramp, and hovering,
65
- * focusing or activating a legend row highlights its slice and centres it in the ring.
66
- *
67
- * @example
68
- * <ui-donut [slices]="byAssetClass()" label="Par classe d'actif" othersLabel="Autres" [valueFormat]="formatEur" [shareFormat]="formatShare" (sliceSelect)="goToHoldings($event)" />
69
- */
70
- class UiDonut {
71
- slices = input.required(/* @ts-ignore */
72
- ...(ngDevMode ? [{ debugName: "slices" }] : /* istanbul ignore next */ []));
73
- label = input.required(/* @ts-ignore */
74
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
75
- othersLabel = input.required(/* @ts-ignore */
76
- ...(ngDevMode ? [{ debugName: "othersLabel" }] : /* istanbul ignore next */ []));
77
- valueFormat = input(identityValue, /* @ts-ignore */
78
- ...(ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []));
79
- shareFormat = input(identityShare, /* @ts-ignore */
80
- ...(ngDevMode ? [{ debugName: "shareFormat" }] : /* istanbul ignore next */ []));
81
- legendHref = input(/* @ts-ignore */
82
- ...(ngDevMode ? [undefined, { debugName: "legendHref" }] : /* istanbul ignore next */ []));
83
- sliceSelect = output();
84
- activeId = signal(null, /* @ts-ignore */
85
- ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
86
- ranked = computed(() => rankSlices(this.slices(), this.othersLabel()), /* @ts-ignore */
87
- ...(ngDevMode ? [{ debugName: "ranked" }] : /* istanbul ignore next */ []));
88
- positioned = computed(() => positionSlices(this.ranked()), /* @ts-ignore */
89
- ...(ngDevMode ? [{ debugName: "positioned" }] : /* istanbul ignore next */ []));
90
- active = computed(() => {
91
- const ranked = this.ranked();
92
- const id = this.activeId();
93
- return ranked.find((slice) => slice.id === id) ?? ranked[0] ?? null;
94
- }, /* @ts-ignore */
95
- ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
96
- ariaLabel = computed(() => {
97
- const detail = shareSlices(this.slices())
98
- .map((slice) => `${slice.label} ${this.shareFormat()(slice.share)}`)
99
- .join(', ');
100
- return detail ? `${this.label()} : ${detail}` : this.label();
101
- }, /* @ts-ignore */
102
- ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
103
- radius = RADIUS;
104
- ringWidth = RING_WIDTH;
105
- strokeColor(slice) {
106
- return `var(--ramp-${slice.ramp})`;
107
- }
108
- sliceClasses(slice) {
109
- const id = this.activeId();
110
- if (id === null) {
111
- return SLICE_CLASSES;
112
- }
113
- return `${SLICE_CLASSES} ${id === slice.id ? 'scale-104 motion-reduce:scale-100' : 'opacity-50'}`;
114
- }
115
- dotClasses(slice) {
116
- return `size-3 flex-none rounded-pill shadow-[inset_0_0_0_1px_var(--border)] ${RAMP_DOT_CLASSES[slice.ramp]}`;
117
- }
118
- rowClasses(slice) {
119
- return `${ROW_CLASSES}${this.isActive(slice) ? ' bg-(--soft)' : ''}`;
120
- }
121
- subLabel(slice) {
122
- return slice.members.length > 0 ? slice.members.join(', ') : (slice.sublabel ?? '');
123
- }
124
- isActive(slice) {
125
- return this.active()?.id === slice.id;
126
- }
127
- highlight(id) {
128
- this.activeId.set(id);
129
- }
130
- clearHighlight(id) {
131
- if (this.activeId() === id) {
132
- this.activeId.set(null);
133
- }
134
- }
135
- hrefFor(id) {
136
- return this.legendHref()?.(id) ?? null;
137
- }
138
- follow(event, slice) {
139
- if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
140
- return;
141
- }
142
- event.preventDefault();
143
- this.select(slice);
144
- }
145
- select(slice) {
146
- this.activeId.set(slice.id);
147
- this.sliceSelect.emit(slice.id);
148
- }
149
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDonut, deps: [], target: i0.ɵɵFactoryTarget.Component });
150
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiDonut, isStandalone: true, selector: "ui-donut", inputs: { slices: { classPropertyName: "slices", publicName: "slices", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, othersLabel: { classPropertyName: "othersLabel", publicName: "othersLabel", isSignal: true, isRequired: true, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, shareFormat: { classPropertyName: "shareFormat", publicName: "shareFormat", isSignal: true, isRequired: false, transformFunction: null }, legendHref: { classPropertyName: "legendHref", publicName: "legendHref", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sliceSelect: "sliceSelect" }, host: { classAttribute: "flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8" }, ngImport: i0, template: `
61
+ var UiDonut = class UiDonut {
62
+ slices = input.required(...ngDevMode ? [{ debugName: "slices" }] : /* istanbul ignore next */ []);
63
+ label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
64
+ othersLabel = input.required(...ngDevMode ? [{ debugName: "othersLabel" }] : /* istanbul ignore next */ []);
65
+ valueFormat = input(identityValue, ...ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []);
66
+ shareFormat = input(identityShare, ...ngDevMode ? [{ debugName: "shareFormat" }] : /* istanbul ignore next */ []);
67
+ legendHref = input(...ngDevMode ? [void 0, { debugName: "legendHref" }] : /* istanbul ignore next */ []);
68
+ sliceSelect = output();
69
+ activeId = signal(null, ...ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []);
70
+ ranked = computed(() => rankSlices(this.slices(), this.othersLabel()), ...ngDevMode ? [{ debugName: "ranked" }] : /* istanbul ignore next */ []);
71
+ positioned = computed(() => positionSlices(this.ranked()), ...ngDevMode ? [{ debugName: "positioned" }] : /* istanbul ignore next */ []);
72
+ active = computed(() => {
73
+ const ranked = this.ranked();
74
+ const id = this.activeId();
75
+ return ranked.find((slice) => slice.id === id) ?? ranked[0] ?? null;
76
+ }, ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
77
+ ariaLabel = computed(() => {
78
+ const detail = shareSlices(this.slices()).map((slice) => `${slice.label} ${this.shareFormat()(slice.share)}`).join(", ");
79
+ return detail ? `${this.label()} : ${detail}` : this.label();
80
+ }, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
81
+ radius = 78;
82
+ ringWidth = 30;
83
+ strokeColor(slice) {
84
+ return `var(--ramp-${slice.ramp})`;
85
+ }
86
+ sliceClasses(slice) {
87
+ const id = this.activeId();
88
+ if (id === null) return SLICE_CLASSES;
89
+ return `${SLICE_CLASSES} ${id === slice.id ? "scale-104 motion-reduce:scale-100" : "opacity-50"}`;
90
+ }
91
+ dotClasses(slice) {
92
+ return `size-3 flex-none rounded-pill shadow-[inset_0_0_0_1px_var(--border)] ${RAMP_DOT_CLASSES[slice.ramp]}`;
93
+ }
94
+ rowClasses(slice) {
95
+ return `${ROW_CLASSES}${this.isActive(slice) ? " bg-(--soft)" : ""}`;
96
+ }
97
+ subLabel(slice) {
98
+ return slice.members.length > 0 ? slice.members.join(", ") : slice.sublabel ?? "";
99
+ }
100
+ isActive(slice) {
101
+ return this.active()?.id === slice.id;
102
+ }
103
+ highlight(id) {
104
+ this.activeId.set(id);
105
+ }
106
+ clearHighlight(id) {
107
+ if (this.activeId() === id) this.activeId.set(null);
108
+ }
109
+ hrefFor(id) {
110
+ return this.legendHref()?.(id) ?? null;
111
+ }
112
+ follow(event, slice) {
113
+ if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
114
+ event.preventDefault();
115
+ this.select(slice);
116
+ }
117
+ select(slice) {
118
+ this.activeId.set(slice.id);
119
+ this.sliceSelect.emit(slice.id);
120
+ }
121
+ static ɵfac = i0.ɵɵngDeclareFactory({
122
+ minVersion: "12.0.0",
123
+ version: "22.2.0",
124
+ ngImport: i0,
125
+ type: UiDonut,
126
+ deps: [],
127
+ target: i0.ɵɵFactoryTarget.Component
128
+ });
129
+ static ɵcmp = i0.ɵɵngDeclareComponent({
130
+ minVersion: "17.0.0",
131
+ version: "22.2.0",
132
+ type: UiDonut,
133
+ isStandalone: true,
134
+ selector: "ui-donut",
135
+ inputs: {
136
+ slices: {
137
+ classPropertyName: "slices",
138
+ publicName: "slices",
139
+ isSignal: true,
140
+ isRequired: true,
141
+ transformFunction: null
142
+ },
143
+ label: {
144
+ classPropertyName: "label",
145
+ publicName: "label",
146
+ isSignal: true,
147
+ isRequired: true,
148
+ transformFunction: null
149
+ },
150
+ othersLabel: {
151
+ classPropertyName: "othersLabel",
152
+ publicName: "othersLabel",
153
+ isSignal: true,
154
+ isRequired: true,
155
+ transformFunction: null
156
+ },
157
+ valueFormat: {
158
+ classPropertyName: "valueFormat",
159
+ publicName: "valueFormat",
160
+ isSignal: true,
161
+ isRequired: false,
162
+ transformFunction: null
163
+ },
164
+ shareFormat: {
165
+ classPropertyName: "shareFormat",
166
+ publicName: "shareFormat",
167
+ isSignal: true,
168
+ isRequired: false,
169
+ transformFunction: null
170
+ },
171
+ legendHref: {
172
+ classPropertyName: "legendHref",
173
+ publicName: "legendHref",
174
+ isSignal: true,
175
+ isRequired: false,
176
+ transformFunction: null
177
+ }
178
+ },
179
+ outputs: { sliceSelect: "sliceSelect" },
180
+ host: { classAttribute: "flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8" },
181
+ ngImport: i0,
182
+ template: `
151
183
  <div class="relative size-[232px] flex-none sm:size-60">
152
184
  <svg class="block h-full w-full -rotate-90" role="img" viewBox="0 0 200 200" [attr.aria-label]="ariaLabel()">
153
185
  <circle
@@ -236,13 +268,30 @@ class UiDonut {
236
268
  <span class="text-caption tracking-normal text-(--muted-foreground)">{{ valueFormat()(slice.value) }}</span>
237
269
  </span>
238
270
  </ng-template>
239
- `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
240
- }
241
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDonut, decorators: [{
242
- type: Component,
243
- args: [{
244
- selector: 'ui-donut',
245
- template: `
271
+ `,
272
+ isInline: true,
273
+ dependencies: [{
274
+ kind: "directive",
275
+ type: NgTemplateOutlet,
276
+ selector: "[ngTemplateOutlet]",
277
+ inputs: [
278
+ "ngTemplateOutletContext",
279
+ "ngTemplateOutlet",
280
+ "ngTemplateOutletInjector"
281
+ ]
282
+ }]
283
+ });
284
+ };
285
+ i0.ɵɵngDeclareClassMetadata({
286
+ minVersion: "12.0.0",
287
+ version: "22.2.0",
288
+ ngImport: i0,
289
+ type: UiDonut,
290
+ decorators: [{
291
+ type: Component,
292
+ args: [{
293
+ selector: "ui-donut",
294
+ template: `
246
295
  <div class="relative size-[232px] flex-none sm:size-60">
247
296
  <svg class="block h-full w-full -rotate-90" role="img" viewBox="0 0 200 200" [attr.aria-label]="ariaLabel()">
248
297
  <circle
@@ -332,16 +381,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
332
381
  </span>
333
382
  </ng-template>
334
383
  `,
335
- imports: [NgTemplateOutlet],
336
- host: {
337
- class: 'flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8',
338
- },
339
- }]
340
- }], propDecorators: { slices: [{ type: i0.Input, args: [{ isSignal: true, alias: "slices", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], othersLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "othersLabel", required: true }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], shareFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "shareFormat", required: false }] }], legendHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "legendHref", required: false }] }], sliceSelect: [{ type: i0.Output, args: ["sliceSelect"] }] } });
341
-
342
- /**
343
- * Generated bundle index. Do not edit.
344
- */
345
-
384
+ imports: [NgTemplateOutlet],
385
+ host: { class: "flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:gap-8" }
386
+ }]
387
+ }],
388
+ propDecorators: {
389
+ slices: [{
390
+ type: i0.Input,
391
+ args: [{
392
+ isSignal: true,
393
+ alias: "slices",
394
+ required: true
395
+ }]
396
+ }],
397
+ label: [{
398
+ type: i0.Input,
399
+ args: [{
400
+ isSignal: true,
401
+ alias: "label",
402
+ required: true
403
+ }]
404
+ }],
405
+ othersLabel: [{
406
+ type: i0.Input,
407
+ args: [{
408
+ isSignal: true,
409
+ alias: "othersLabel",
410
+ required: true
411
+ }]
412
+ }],
413
+ valueFormat: [{
414
+ type: i0.Input,
415
+ args: [{
416
+ isSignal: true,
417
+ alias: "valueFormat",
418
+ required: false
419
+ }]
420
+ }],
421
+ shareFormat: [{
422
+ type: i0.Input,
423
+ args: [{
424
+ isSignal: true,
425
+ alias: "shareFormat",
426
+ required: false
427
+ }]
428
+ }],
429
+ legendHref: [{
430
+ type: i0.Input,
431
+ args: [{
432
+ isSignal: true,
433
+ alias: "legendHref",
434
+ required: false
435
+ }]
436
+ }],
437
+ sliceSelect: [{
438
+ type: i0.Output,
439
+ args: ["sliceSelect"]
440
+ }]
441
+ }
442
+ });
346
443
  export { UiDonut };
347
- //# sourceMappingURL=joanroucoux-cairn-ui-donut.mjs.map
444
+
445
+ //# sourceMappingURL=joanroucoux-cairn-ui-donut.mjs.map