@joanroucoux/cairn-ui 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs +54 -0
- package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-alert.mjs +129 -0
- package/fesm2022/joanroucoux-cairn-ui-alert.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-amount.mjs +114 -0
- package/fesm2022/joanroucoux-cairn-ui-amount.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-async.mjs +282 -0
- package/fesm2022/joanroucoux-cairn-ui-async.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-avatar.mjs +71 -0
- package/fesm2022/joanroucoux-cairn-ui-avatar.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-back-link.mjs +79 -0
- package/fesm2022/joanroucoux-cairn-ui-back-link.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-badge.mjs +48 -0
- package/fesm2022/joanroucoux-cairn-ui-badge.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-button.mjs +128 -0
- package/fesm2022/joanroucoux-cairn-ui-button.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-card.mjs +146 -0
- package/fesm2022/joanroucoux-cairn-ui-card.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs +172 -0
- package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-control.mjs +18 -0
- package/fesm2022/joanroucoux-cairn-ui-control.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-delta.mjs +103 -0
- package/fesm2022/joanroucoux-cairn-ui-delta.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-dialog.mjs +626 -0
- package/fesm2022/joanroucoux-cairn-ui-dialog.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-donut.mjs +347 -0
- package/fesm2022/joanroucoux-cairn-ui-donut.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-empty.mjs +47 -0
- package/fesm2022/joanroucoux-cairn-ui-empty.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-external-link.mjs +65 -0
- package/fesm2022/joanroucoux-cairn-ui-external-link.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-fact.mjs +99 -0
- package/fesm2022/joanroucoux-cairn-ui-fact.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-field.mjs +232 -0
- package/fesm2022/joanroucoux-cairn-ui-field.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs +89 -0
- package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-input.mjs +90 -0
- package/fesm2022/joanroucoux-cairn-ui-input.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs +844 -0
- package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-menu.mjs +241 -0
- package/fesm2022/joanroucoux-cairn-ui-menu.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-meter.mjs +86 -0
- package/fesm2022/joanroucoux-cairn-ui-meter.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-motion.mjs +415 -0
- package/fesm2022/joanroucoux-cairn-ui-motion.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs +45 -0
- package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-row.mjs +164 -0
- package/fesm2022/joanroucoux-cairn-ui-row.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-segmented.mjs +165 -0
- package/fesm2022/joanroucoux-cairn-ui-segmented.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-select.mjs +69 -0
- package/fesm2022/joanroucoux-cairn-ui-select.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs +91 -0
- package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs +43 -0
- package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-stat.mjs +96 -0
- package/fesm2022/joanroucoux-cairn-ui-stat.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-switch.mjs +37 -0
- package/fesm2022/joanroucoux-cairn-ui-switch.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs +75 -0
- package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-table.mjs +321 -0
- package/fesm2022/joanroucoux-cairn-ui-table.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui-toast.mjs +153 -0
- package/fesm2022/joanroucoux-cairn-ui-toast.mjs.map +1 -0
- package/fesm2022/joanroucoux-cairn-ui.mjs +0 -5462
- package/fesm2022/joanroucoux-cairn-ui.mjs.map +1 -1
- package/package.json +141 -1
- package/types/joanroucoux-cairn-ui-action-bar.d.ts +22 -0
- package/types/joanroucoux-cairn-ui-alert.d.ts +23 -0
- package/types/joanroucoux-cairn-ui-amount.d.ts +45 -0
- package/types/joanroucoux-cairn-ui-async.d.ts +58 -0
- package/types/joanroucoux-cairn-ui-avatar.d.ts +35 -0
- package/types/joanroucoux-cairn-ui-back-link.d.ts +22 -0
- package/types/joanroucoux-cairn-ui-badge.d.ts +23 -0
- package/types/joanroucoux-cairn-ui-button.d.ts +29 -0
- package/types/joanroucoux-cairn-ui-card.d.ts +36 -0
- package/types/joanroucoux-cairn-ui-choice-chips.d.ts +46 -0
- package/types/joanroucoux-cairn-ui-control.d.ts +27 -0
- package/types/joanroucoux-cairn-ui-delta.d.ts +38 -0
- package/types/joanroucoux-cairn-ui-dialog.d.ts +72 -0
- package/types/joanroucoux-cairn-ui-donut.d.ts +59 -0
- package/types/joanroucoux-cairn-ui-empty.d.ts +19 -0
- package/types/joanroucoux-cairn-ui-external-link.d.ts +15 -0
- package/types/joanroucoux-cairn-ui-fact.d.ts +44 -0
- package/types/joanroucoux-cairn-ui-field.d.ts +84 -0
- package/types/joanroucoux-cairn-ui-filter-chips.d.ts +28 -0
- package/types/joanroucoux-cairn-ui-input.d.ts +51 -0
- package/types/joanroucoux-cairn-ui-line-chart.d.ts +114 -0
- package/types/joanroucoux-cairn-ui-menu.d.ts +66 -0
- package/types/joanroucoux-cairn-ui-meter.d.ts +30 -0
- package/types/joanroucoux-cairn-ui-motion.d.ts +124 -0
- package/types/joanroucoux-cairn-ui-nav-item.d.ts +21 -0
- package/types/joanroucoux-cairn-ui-row.d.ts +78 -0
- package/types/joanroucoux-cairn-ui-segmented.d.ts +36 -0
- package/types/joanroucoux-cairn-ui-select.d.ts +23 -0
- package/types/joanroucoux-cairn-ui-skeleton.d.ts +26 -0
- package/types/joanroucoux-cairn-ui-stale-link.d.ts +24 -0
- package/types/joanroucoux-cairn-ui-stat.d.ts +32 -0
- package/types/joanroucoux-cairn-ui-switch.d.ts +17 -0
- package/types/joanroucoux-cairn-ui-tab-bar.d.ts +31 -0
- package/types/joanroucoux-cairn-ui-table.d.ts +139 -0
- package/types/joanroucoux-cairn-ui-toast.d.ts +51 -0
- package/types/joanroucoux-cairn-ui.d.ts +1 -1440
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- The package root `@joanroucoux/cairn-ui` no longer exports anything. Each component folder is now its own
|
|
8
|
+
entry point, imported as `@joanroucoux/cairn-ui/<folder>`. A consumer's bundler splits chunks per module: a
|
|
9
|
+
single bundle, or a root barrel re-exporting the entries, put every component in the initial chunk. Moving to
|
|
10
|
+
subpaths lets each lazy route pull in only the components it uses.
|
|
11
|
+
- Migration: replace `import { UiButton } from '@joanroucoux/cairn-ui'` with
|
|
12
|
+
`import { UiButton } from '@joanroucoux/cairn-ui/button'`. The stylesheets under `@joanroucoux/cairn-ui/styles/*`
|
|
13
|
+
are unchanged.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `@joanroucoux/cairn-ui/control` (`UI_CONTROL`, `UiControl`, `UiControlError`) and, from
|
|
18
|
+
`@joanroucoux/cairn-ui/input`, `CONTROL_BASE_CLASSES`, `CONTROL_SIZE_CLASSES` and `CONTROL_SURFACE_CLASSES` are now
|
|
19
|
+
exported, because `select`, `choice-chips` and `field` import them across entry points. Only `UiControlError` was
|
|
20
|
+
public before.
|
|
21
|
+
|
|
22
|
+
### Subpaths
|
|
23
|
+
|
|
24
|
+
Every symbol that was exported from the root now lives under exactly one subpath:
|
|
25
|
+
|
|
26
|
+
- `@joanroucoux/cairn-ui/action-bar`: `UiActionBar`
|
|
27
|
+
- `@joanroucoux/cairn-ui/alert`: `ALERT_VARIANTS`, `UiAlert`, `AlertVariant`
|
|
28
|
+
- `@joanroucoux/cairn-ui/amount`: `AMOUNT_MASKINGS`, `AMOUNT_NUMERICS`, `UI_AMOUNT_MASKED`, `UiAmount`, `AmountMasking`, `AmountNumeric`, `AmountFormatOptions`, `formatAmount`
|
|
29
|
+
- `@joanroucoux/cairn-ui/async`: `ASYNC_ALIGNS`, `ASYNC_STATES`, `ASYNC_VARIANTS`, `UiAsync`, `AsyncAlign`, `AsyncState`, `AsyncVariant`, `delayedState`
|
|
30
|
+
- `@joanroucoux/cairn-ui/avatar`: `AVATAR_SIZES`, `UiAvatar`, `UiAvatarLink`, `AvatarSize`
|
|
31
|
+
- `@joanroucoux/cairn-ui/back-link`: `BACK_LINK_SIZES`, `UiBackLink`, `BackLinkSize`
|
|
32
|
+
- `@joanroucoux/cairn-ui/badge`: `BADGE_SIZES`, `BADGE_VARIANTS`, `UiBadge`, `BadgeSize`, `BadgeVariant`
|
|
33
|
+
- `@joanroucoux/cairn-ui/button`: `BUTTON_SIZES`, `BUTTON_VARIANTS`, `UiButton`, `ButtonSize`, `ButtonVariant`
|
|
34
|
+
- `@joanroucoux/cairn-ui/card`: `CARD_PADDINGS`, `CARD_SURFACES`, `CARD_VARIANTS`, `UiCard`, `CardPadding`, `CardSurface`, `CardVariant`
|
|
35
|
+
- `@joanroucoux/cairn-ui/choice-chips`: `ChoiceChipOption`, `UiChoiceChips`
|
|
36
|
+
- `@joanroucoux/cairn-ui/control`: `UiControlError`, `UI_CONTROL`, `UiControl`
|
|
37
|
+
- `@joanroucoux/cairn-ui/delta`: `DELTA_EMPHASES`, `DELTA_SIZES`, `DELTA_WEIGHTS`, `UiDelta`, `DeltaEmphasis`, `DeltaSize`, `DeltaWeight`
|
|
38
|
+
- `@joanroucoux/cairn-ui/dialog`: `DIALOG_CLOSE_REASONS`, `DIALOG_LAYOUTS`, `DIALOG_WIDTHS`, `UiDialog`, `DialogCloseReason`, `DialogLayout`, `DialogWidth`
|
|
39
|
+
- `@joanroucoux/cairn-ui/donut`: `DonutSlice`, `UiDonut`
|
|
40
|
+
- `@joanroucoux/cairn-ui/empty`: `UiEmpty`
|
|
41
|
+
- `@joanroucoux/cairn-ui/external-link`: `UiExternalLink`
|
|
42
|
+
- `@joanroucoux/cairn-ui/fact`: `FACT_SIZES`, `FACT_SUB_TONES`, `UiFact`, `UiFacts`, `FactSize`, `FactSubTone`
|
|
43
|
+
- `@joanroucoux/cairn-ui/field`: `UiField`, `UiFieldLeading`, `UiFieldTrailing`
|
|
44
|
+
- `@joanroucoux/cairn-ui/filter-chips`: `FilterChipOption`, `UiFilterChips`
|
|
45
|
+
- `@joanroucoux/cairn-ui/input`: `CONTROL_SIZES`, `CONTROL_SURFACES`, `UiInput`, `UiTextarea`, `ControlSize`, `ControlSurface`, `CONTROL_BASE_CLASSES`, `CONTROL_SIZE_CLASSES`, `CONTROL_SURFACE_CLASSES`
|
|
46
|
+
- `@joanroucoux/cairn-ui/line-chart`: `AxisTicks`, `ChartPoint`, `TooltipSize`, `UiLineChart`
|
|
47
|
+
- `@joanroucoux/cairn-ui/menu`: `UiMenu`, `UiMenuItem`, `UiMenuTrigger`
|
|
48
|
+
- `@joanroucoux/cairn-ui/meter`: `METER_SIZES`, `METER_TONES`, `UiMeter`, `MeterSize`, `MeterTone`
|
|
49
|
+
- `@joanroucoux/cairn-ui/motion`: `UiFlipItem`, `UiFlipList`, `UiHighlight`, `injectReducedMotion`, `holdTransitionsUntilRendered`
|
|
50
|
+
- `@joanroucoux/cairn-ui/nav-item`: `UiNavItem`
|
|
51
|
+
- `@joanroucoux/cairn-ui/row`: `ROW_GAPS`, `ROW_PADDINGS`, `ROW_SIZES`, `UiListRow`, `UiRow`, `UiRowItem`, `UiRowTile`, `RowGap`, `RowPadding`, `RowSize`
|
|
52
|
+
- `@joanroucoux/cairn-ui/segmented`: `SEGMENTED_SIZES`, `UiSegmented`, `SegmentedOption`, `SegmentedSize`
|
|
53
|
+
- `@joanroucoux/cairn-ui/select`: `UiSelect`
|
|
54
|
+
- `@joanroucoux/cairn-ui/skeleton`: `SKELETON_SHAPES`, `UiSkeleton`, `SkeletonShape`
|
|
55
|
+
- `@joanroucoux/cairn-ui/stale-link`: `STALE_LINK_SIZES`, `UiStaleLink`, `StaleLinkSize`
|
|
56
|
+
- `@joanroucoux/cairn-ui/stat`: `STAT_SIZES`, `UiStat`, `StatSize`
|
|
57
|
+
- `@joanroucoux/cairn-ui/switch`: `UiSwitch`
|
|
58
|
+
- `@joanroucoux/cairn-ui/tab-bar`: `UiTab`, `UiTabBar`
|
|
59
|
+
- `@joanroucoux/cairn-ui/table`: `CELL_BREAKPOINTS`, `GROUP_SIZES`, `SUB_TONES`, `TABLE_ROWS`, `UiCellSub`, `UiGroup`, `UiGroupCell`, `UiRowAction`, `UiRowLink`, `UiTable`, `UiTd`, `UiTh`, `UiTr`, `CellBreakpoint`, `GroupSize`, `SubTone`, `TableRow`
|
|
60
|
+
- `@joanroucoux/cairn-ui/toast`: `UiToaster`, `UiToasts`, `Toast`
|
|
61
|
+
|
|
3
62
|
## 0.7.2
|
|
4
63
|
|
|
5
64
|
### Added
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DOCUMENT } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, ElementRef, DestroyRef, afterNextRender, Component } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
const HEIGHT_PROPERTY = '--action-bar-height';
|
|
6
|
+
/**
|
|
7
|
+
* Bar of actions fixed above the tab bar on iPhone: the primary actions of a detail screen, within
|
|
8
|
+
* thumb reach. It sits `--tab-bar-height` (52px plus the safe area) above the bottom edge, so it clears `ui-tab-bar`, and
|
|
9
|
+
* it is hidden from `64rem`, where the same actions live in the page itself. Its children share the
|
|
10
|
+
* width equally. While it is on the page it publishes its height as `--action-bar-height` on the
|
|
11
|
+
* root element, which `ui-toaster` adds to its offset so a confirmation never covers the actions.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <ui-action-bar>
|
|
15
|
+
* <button ui-button variant="outline" size="tall">Sell</button>
|
|
16
|
+
* <button ui-button size="tall">Buy</button>
|
|
17
|
+
* </ui-action-bar>
|
|
18
|
+
*/
|
|
19
|
+
class UiActionBar {
|
|
20
|
+
constructor() {
|
|
21
|
+
const host = inject(ElementRef).nativeElement;
|
|
22
|
+
const root = inject(DOCUMENT).documentElement;
|
|
23
|
+
const destroyRef = inject(DestroyRef);
|
|
24
|
+
const publish = () => root.style.setProperty(HEIGHT_PROPERTY, `${host.offsetHeight}px`);
|
|
25
|
+
afterNextRender(() => {
|
|
26
|
+
publish();
|
|
27
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
28
|
+
const observer = new ResizeObserver(publish);
|
|
29
|
+
observer.observe(host);
|
|
30
|
+
destroyRef.onDestroy(() => observer.disconnect());
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
destroyRef.onDestroy(() => root.style.removeProperty(HEIGHT_PROPERTY));
|
|
34
|
+
}
|
|
35
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiActionBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
36
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: UiActionBar, isStandalone: true, selector: "ui-action-bar", host: { classAttribute: "fixed inset-x-0 bottom-[var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
37
|
+
}
|
|
38
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiActionBar, decorators: [{
|
|
39
|
+
type: Component,
|
|
40
|
+
args: [{
|
|
41
|
+
selector: 'ui-action-bar',
|
|
42
|
+
template: '<ng-content />',
|
|
43
|
+
host: {
|
|
44
|
+
class: 'fixed inset-x-0 bottom-[var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden',
|
|
45
|
+
},
|
|
46
|
+
}]
|
|
47
|
+
}], ctorParameters: () => [] });
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generated bundle index. Do not edit.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
export { UiActionBar };
|
|
54
|
+
//# sourceMappingURL=joanroucoux-cairn-ui-action-bar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joanroucoux-cairn-ui-action-bar.mjs","sources":["../../../projects/ui/action-bar/action-bar.ts","../../../projects/ui/action-bar/joanroucoux-cairn-ui-action-bar.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { Component, DestroyRef, ElementRef, afterNextRender, inject } from '@angular/core';\n\nconst HEIGHT_PROPERTY = '--action-bar-height';\n\n/**\n * Bar of actions fixed above the tab bar on iPhone: the primary actions of a detail screen, within\n * thumb reach. It sits `--tab-bar-height` (52px plus the safe area) above the bottom edge, so it clears `ui-tab-bar`, and\n * it is hidden from `64rem`, where the same actions live in the page itself. Its children share the\n * width equally. While it is on the page it publishes its height as `--action-bar-height` on the\n * root element, which `ui-toaster` adds to its offset so a confirmation never covers the actions.\n *\n * @example\n * <ui-action-bar>\n * <button ui-button variant=\"outline\" size=\"tall\">Sell</button>\n * <button ui-button size=\"tall\">Buy</button>\n * </ui-action-bar>\n */\n@Component({\n selector: 'ui-action-bar',\n template: '<ng-content />',\n host: {\n class:\n 'fixed inset-x-0 bottom-[var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden',\n },\n})\nexport class UiActionBar {\n constructor() {\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const root = inject(DOCUMENT).documentElement;\n const destroyRef = inject(DestroyRef);\n const publish = (): void => root.style.setProperty(HEIGHT_PROPERTY, `${host.offsetHeight}px`);\n\n afterNextRender(() => {\n publish();\n\n if (typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver(publish);\n observer.observe(host);\n destroyRef.onDestroy(() => observer.disconnect());\n }\n });\n\n destroyRef.onDestroy(() => root.style.removeProperty(HEIGHT_PROPERTY));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAGA,MAAM,eAAe,GAAG,qBAAqB;AAE7C;;;;;;;;;;;;AAYG;MASU,WAAW,CAAA;AACtB,IAAA,WAAA,GAAA;QACE,MAAM,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC,aAAa;QACtE,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,eAAe;AAC7C,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AACrC,QAAA,MAAM,OAAO,GAAG,MAAY,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,EAAE,CAAA,EAAG,IAAI,CAAC,YAAY,CAAA,EAAA,CAAI,CAAC;QAE7F,eAAe,CAAC,MAAK;AACnB,YAAA,OAAO,EAAE;AAET,YAAA,IAAI,OAAO,cAAc,KAAK,WAAW,EAAE;AACzC,gBAAA,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC;AAC5C,gBAAA,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;gBACtB,UAAU,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;YACnD;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,UAAU,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;IACxE;uGAlBW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,wTANZ,gBAAgB,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAMf,WAAW,EAAA,UAAA,EAAA,CAAA;kBARvB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,eAAe;AACzB,oBAAA,QAAQ,EAAE,gBAAgB;AAC1B,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EACH,mNAAmN;AACtN,qBAAA;AACF,iBAAA;;;ACzBD;;AAEG;;;;"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, booleanAttribute, computed, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const ALERT_VARIANTS = ['error', 'warning'];
|
|
5
|
+
const BASE_CLASSES = 'flex items-start gap-2.5 text-left text-label';
|
|
6
|
+
const VARIANT_CLASSES = {
|
|
7
|
+
error: 'rounded-control bg-(--card) px-3.5 py-3 shadow-[inset_0_0_0_1px_var(--border)]',
|
|
8
|
+
warning: 'text-(--foreground)',
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Inline message explaining a failure or a consequence: an icon, an optional heading and a text.
|
|
12
|
+
* It fades in when it appears; set `[fadeIn]="false"` on an alert that is part of the page as it
|
|
13
|
+
* opens (an error state after a load), which shows without motion.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <ui-alert heading="Sign-in failed">Try again.</ui-alert>
|
|
17
|
+
*/
|
|
18
|
+
class UiAlert {
|
|
19
|
+
variant = input('error', /* @ts-ignore */
|
|
20
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
21
|
+
heading = input(/* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [undefined, { debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
23
|
+
fadeIn = input(true, { ...(ngDevMode ? { debugName: "fadeIn" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
24
|
+
classes = computed(() => `${BASE_CLASSES} ${VARIANT_CLASSES[this.variant()]}${this.fadeIn() ? ' ui-enter-fade' : ''}`, /* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
26
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAlert, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
27
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiAlert, isStandalone: true, selector: "ui-alert", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, fadeIn: { classPropertyName: "fadeIn", publicName: "fadeIn", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "animate.leave": "ui-leave-fade" }, properties: { "class": "classes()", "attr.role": "variant() === 'warning' ? 'status' : 'alert'" } }, ngImport: i0, template: `
|
|
28
|
+
@if (variant() === 'warning') {
|
|
29
|
+
<svg
|
|
30
|
+
aria-hidden="true"
|
|
31
|
+
class="mt-px block flex-none stroke-(--negative)"
|
|
32
|
+
fill="none"
|
|
33
|
+
height="18"
|
|
34
|
+
stroke-linecap="round"
|
|
35
|
+
stroke-linejoin="round"
|
|
36
|
+
stroke-width="1.75"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
width="18"
|
|
39
|
+
>
|
|
40
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
41
|
+
<path d="M12 9v4" />
|
|
42
|
+
<path d="M12 17h.01" />
|
|
43
|
+
</svg>
|
|
44
|
+
} @else {
|
|
45
|
+
<svg
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
class="block flex-none stroke-(--negative)"
|
|
48
|
+
fill="none"
|
|
49
|
+
height="18"
|
|
50
|
+
stroke-linecap="round"
|
|
51
|
+
stroke-linejoin="round"
|
|
52
|
+
stroke-width="1.75"
|
|
53
|
+
viewBox="0 0 24 24"
|
|
54
|
+
width="18"
|
|
55
|
+
[class.mt-px]="heading()"
|
|
56
|
+
>
|
|
57
|
+
<circle cx="12" cy="12" r="10" />
|
|
58
|
+
<line x1="12" x2="12" y1="8" y2="12" />
|
|
59
|
+
<line x1="12" x2="12.01" y1="16" y2="16" />
|
|
60
|
+
</svg>
|
|
61
|
+
}
|
|
62
|
+
<div class="flex flex-col gap-0.5">
|
|
63
|
+
@if (heading()) {
|
|
64
|
+
<span class="font-medium">{{ heading() }}</span>
|
|
65
|
+
}
|
|
66
|
+
<span class="text-pretty" [class.text-(--muted-foreground)]="heading()"><ng-content /></span>
|
|
67
|
+
</div>
|
|
68
|
+
`, isInline: true });
|
|
69
|
+
}
|
|
70
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAlert, decorators: [{
|
|
71
|
+
type: Component,
|
|
72
|
+
args: [{
|
|
73
|
+
selector: 'ui-alert',
|
|
74
|
+
template: `
|
|
75
|
+
@if (variant() === 'warning') {
|
|
76
|
+
<svg
|
|
77
|
+
aria-hidden="true"
|
|
78
|
+
class="mt-px block flex-none stroke-(--negative)"
|
|
79
|
+
fill="none"
|
|
80
|
+
height="18"
|
|
81
|
+
stroke-linecap="round"
|
|
82
|
+
stroke-linejoin="round"
|
|
83
|
+
stroke-width="1.75"
|
|
84
|
+
viewBox="0 0 24 24"
|
|
85
|
+
width="18"
|
|
86
|
+
>
|
|
87
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
88
|
+
<path d="M12 9v4" />
|
|
89
|
+
<path d="M12 17h.01" />
|
|
90
|
+
</svg>
|
|
91
|
+
} @else {
|
|
92
|
+
<svg
|
|
93
|
+
aria-hidden="true"
|
|
94
|
+
class="block flex-none stroke-(--negative)"
|
|
95
|
+
fill="none"
|
|
96
|
+
height="18"
|
|
97
|
+
stroke-linecap="round"
|
|
98
|
+
stroke-linejoin="round"
|
|
99
|
+
stroke-width="1.75"
|
|
100
|
+
viewBox="0 0 24 24"
|
|
101
|
+
width="18"
|
|
102
|
+
[class.mt-px]="heading()"
|
|
103
|
+
>
|
|
104
|
+
<circle cx="12" cy="12" r="10" />
|
|
105
|
+
<line x1="12" x2="12" y1="8" y2="12" />
|
|
106
|
+
<line x1="12" x2="12.01" y1="16" y2="16" />
|
|
107
|
+
</svg>
|
|
108
|
+
}
|
|
109
|
+
<div class="flex flex-col gap-0.5">
|
|
110
|
+
@if (heading()) {
|
|
111
|
+
<span class="font-medium">{{ heading() }}</span>
|
|
112
|
+
}
|
|
113
|
+
<span class="text-pretty" [class.text-(--muted-foreground)]="heading()"><ng-content /></span>
|
|
114
|
+
</div>
|
|
115
|
+
`,
|
|
116
|
+
host: {
|
|
117
|
+
'animate.leave': 'ui-leave-fade',
|
|
118
|
+
'[class]': 'classes()',
|
|
119
|
+
'[attr.role]': "variant() === 'warning' ? 'status' : 'alert'",
|
|
120
|
+
},
|
|
121
|
+
}]
|
|
122
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], fadeIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "fadeIn", required: false }] }] } });
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generated bundle index. Do not edit.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
export { ALERT_VARIANTS, UiAlert };
|
|
129
|
+
//# sourceMappingURL=joanroucoux-cairn-ui-alert.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joanroucoux-cairn-ui-alert.mjs","sources":["../../../projects/ui/alert/alert.ts","../../../projects/ui/alert/joanroucoux-cairn-ui-alert.ts"],"sourcesContent":["import { Component, booleanAttribute, computed, input } from '@angular/core';\n\nexport const ALERT_VARIANTS = ['error', 'warning'] as const;\nexport type AlertVariant = (typeof ALERT_VARIANTS)[number];\n\nconst BASE_CLASSES = 'flex items-start gap-2.5 text-left text-label';\n\nconst VARIANT_CLASSES: Record<AlertVariant, string> = {\n error: 'rounded-control bg-(--card) px-3.5 py-3 shadow-[inset_0_0_0_1px_var(--border)]',\n warning: 'text-(--foreground)',\n};\n\n/**\n * Inline message explaining a failure or a consequence: an icon, an optional heading and a text.\n * It fades in when it appears; set `[fadeIn]=\"false\"` on an alert that is part of the page as it\n * opens (an error state after a load), which shows without motion.\n *\n * @example\n * <ui-alert heading=\"Sign-in failed\">Try again.</ui-alert>\n */\n@Component({\n selector: 'ui-alert',\n template: `\n @if (variant() === 'warning') {\n <svg\n aria-hidden=\"true\"\n class=\"mt-px block flex-none stroke-(--negative)\"\n fill=\"none\"\n height=\"18\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 24 24\"\n width=\"18\"\n >\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n } @else {\n <svg\n aria-hidden=\"true\"\n class=\"block flex-none stroke-(--negative)\"\n fill=\"none\"\n height=\"18\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 24 24\"\n width=\"18\"\n [class.mt-px]=\"heading()\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\" />\n </svg>\n }\n <div class=\"flex flex-col gap-0.5\">\n @if (heading()) {\n <span class=\"font-medium\">{{ heading() }}</span>\n }\n <span class=\"text-pretty\" [class.text-(--muted-foreground)]=\"heading()\"><ng-content /></span>\n </div>\n `,\n host: {\n 'animate.leave': 'ui-leave-fade',\n '[class]': 'classes()',\n '[attr.role]': \"variant() === 'warning' ? 'status' : 'alert'\",\n },\n})\nexport class UiAlert {\n readonly variant = input<AlertVariant>('error');\n readonly heading = input<string>();\n readonly fadeIn = input(true, { transform: booleanAttribute });\n\n protected readonly classes = computed(\n () => `${BASE_CLASSES} ${VARIANT_CLASSES[this.variant()]}${this.fadeIn() ? ' ui-enter-fade' : ''}`,\n );\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAEa,cAAc,GAAG,CAAC,OAAO,EAAE,SAAS;AAGjD,MAAM,YAAY,GAAG,+CAA+C;AAEpE,MAAM,eAAe,GAAiC;AACpD,IAAA,KAAK,EAAE,gFAAgF;AACvF,IAAA,OAAO,EAAE,qBAAqB;CAC/B;AAED;;;;;;;AAOG;MAmDU,OAAO,CAAA;IACT,OAAO,GAAG,KAAK,CAAe,OAAO;gFAAC;AACtC,IAAA,OAAO,GAAG,KAAK;2FAAU;IACzB,MAAM,GAAG,KAAK,CAAC,IAAI,8EAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;AAE3C,IAAA,OAAO,GAAG,QAAQ,CACnC,MAAM,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA,EAAG,IAAI,CAAC,MAAM,EAAE,GAAG,gBAAgB,GAAG,EAAE,CAAA,CAAE;gFACnG;uGAPU,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,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,eAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,WAAA,EAAA,WAAA,EAAA,8CAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhDR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAOU,OAAO,EAAA,UAAA,EAAA,CAAA;kBAlDnB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,UAAU;AACpB,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCT,EAAA,CAAA;AACD,oBAAA,IAAI,EAAE;AACJ,wBAAA,eAAe,EAAE,eAAe;AAChC,wBAAA,SAAS,EAAE,WAAW;AACtB,wBAAA,aAAa,EAAE,8CAA8C;AAC9D,qBAAA;AACF,iBAAA;;;ACrED;;AAEG;;;;"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, signal, input, inject, LOCALE_ID, booleanAttribute, computed, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const MINUS = '−';
|
|
5
|
+
const EM_DASH = '—';
|
|
6
|
+
const BULLETS = '••••';
|
|
7
|
+
/** Formats a number or an amount for the locale, with a missing value as an em dash and masking behind four bullets. */
|
|
8
|
+
function formatAmount(value, options, masked = false) {
|
|
9
|
+
if (value === null || value === undefined || Number.isNaN(value)) {
|
|
10
|
+
return EM_DASH;
|
|
11
|
+
}
|
|
12
|
+
const digits = options.fractionDigits ?? 2;
|
|
13
|
+
const format = new Intl.NumberFormat(options.locale, {
|
|
14
|
+
...(options.currency ? { style: 'currency', currency: options.currency } : {}),
|
|
15
|
+
minimumFractionDigits: digits,
|
|
16
|
+
maximumFractionDigits: digits,
|
|
17
|
+
});
|
|
18
|
+
const magnitude = Math.abs(value);
|
|
19
|
+
const parts = format.formatToParts(magnitude);
|
|
20
|
+
if (masked) {
|
|
21
|
+
let replaced = false;
|
|
22
|
+
return parts
|
|
23
|
+
.map((part) => {
|
|
24
|
+
if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {
|
|
25
|
+
if (replaced) {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
replaced = true;
|
|
29
|
+
return BULLETS;
|
|
30
|
+
}
|
|
31
|
+
return part.value;
|
|
32
|
+
})
|
|
33
|
+
.join('');
|
|
34
|
+
}
|
|
35
|
+
const text = parts.map((part) => part.value).join('');
|
|
36
|
+
const isZero = Number(format.format(magnitude).replace(/[^\d]/g, '')) === 0;
|
|
37
|
+
if (!options.signed || isZero) {
|
|
38
|
+
return value < 0 && !isZero ? `${MINUS}${text}` : text;
|
|
39
|
+
}
|
|
40
|
+
return `${value > 0 ? '+' : MINUS}${text}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** App-wide "amounts are hidden" state. Defaults to never masked; the app overrides it. */
|
|
44
|
+
const UI_AMOUNT_MASKED = new InjectionToken('UI_AMOUNT_MASKED', {
|
|
45
|
+
providedIn: 'root',
|
|
46
|
+
factory: () => signal(false),
|
|
47
|
+
});
|
|
48
|
+
/** Available figure styles. `AmountNumeric` is derived from this tuple. */
|
|
49
|
+
const AMOUNT_NUMERICS = ['tabular', 'proportional'];
|
|
50
|
+
/** Available masking behaviors. `AmountMasking` is derived from this tuple. */
|
|
51
|
+
const AMOUNT_MASKINGS = ['dots', 'hide'];
|
|
52
|
+
/**
|
|
53
|
+
* A number or an amount of money, formatted for the locale and masked when the app hides amounts.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <ui-amount [value]="total" currency="EUR" numeric="proportional" />
|
|
57
|
+
* <ui-amount [value]="dayChange" currency="EUR" signed whenMasked="hide" />
|
|
58
|
+
*/
|
|
59
|
+
class UiAmount {
|
|
60
|
+
value = input(/* @ts-ignore */
|
|
61
|
+
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
62
|
+
currency = input(/* @ts-ignore */
|
|
63
|
+
...(ngDevMode ? [undefined, { debugName: "currency" }] : /* istanbul ignore next */ []));
|
|
64
|
+
locale = input(inject(LOCALE_ID), /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
66
|
+
signed = input(false, { ...(ngDevMode ? { debugName: "signed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
67
|
+
fractionDigits = input(2, /* @ts-ignore */
|
|
68
|
+
...(ngDevMode ? [{ debugName: "fractionDigits" }] : /* istanbul ignore next */ []));
|
|
69
|
+
numeric = input('tabular', /* @ts-ignore */
|
|
70
|
+
...(ngDevMode ? [{ debugName: "numeric" }] : /* istanbul ignore next */ []));
|
|
71
|
+
whenMasked = input('dots', /* @ts-ignore */
|
|
72
|
+
...(ngDevMode ? [{ debugName: "whenMasked" }] : /* istanbul ignore next */ []));
|
|
73
|
+
#masked = inject(UI_AMOUNT_MASKED);
|
|
74
|
+
hidden = computed(() => this.#masked() && this.whenMasked() === 'hide', /* @ts-ignore */
|
|
75
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
76
|
+
text = computed(() => formatAmount(this.value(), {
|
|
77
|
+
locale: this.locale(),
|
|
78
|
+
currency: this.currency(),
|
|
79
|
+
signed: this.signed(),
|
|
80
|
+
fractionDigits: this.fractionDigits(),
|
|
81
|
+
}, this.#masked()), /* @ts-ignore */
|
|
82
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
83
|
+
classes = computed(() => {
|
|
84
|
+
const missing = this.value() === null || this.value() === undefined;
|
|
85
|
+
return [
|
|
86
|
+
'whitespace-nowrap',
|
|
87
|
+
this.numeric() === 'tabular' ? 'tabular-nums' : '',
|
|
88
|
+
missing ? 'text-(--subtle-foreground)' : '',
|
|
89
|
+
]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join(' ');
|
|
92
|
+
}, /* @ts-ignore */
|
|
93
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
94
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAmount, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
95
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiAmount, isStandalone: true, selector: "ui-amount", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, signed: { classPropertyName: "signed", publicName: "signed", isSignal: true, isRequired: false, transformFunction: null }, fractionDigits: { classPropertyName: "fractionDigits", publicName: "fractionDigits", isSignal: true, isRequired: false, transformFunction: null }, numeric: { classPropertyName: "numeric", publicName: "numeric", isSignal: true, isRequired: false, transformFunction: null }, whenMasked: { classPropertyName: "whenMasked", publicName: "whenMasked", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.hidden": "hidden() || null" } }, ngImport: i0, template: '{{ text() }}', isInline: true });
|
|
96
|
+
}
|
|
97
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAmount, decorators: [{
|
|
98
|
+
type: Component,
|
|
99
|
+
args: [{
|
|
100
|
+
selector: 'ui-amount',
|
|
101
|
+
template: '{{ text() }}',
|
|
102
|
+
host: {
|
|
103
|
+
'[class]': 'classes()',
|
|
104
|
+
'[attr.hidden]': 'hidden() || null',
|
|
105
|
+
},
|
|
106
|
+
}]
|
|
107
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], signed: [{ type: i0.Input, args: [{ isSignal: true, alias: "signed", required: false }] }], fractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "fractionDigits", required: false }] }], numeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "numeric", required: false }] }], whenMasked: [{ type: i0.Input, args: [{ isSignal: true, alias: "whenMasked", required: false }] }] } });
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generated bundle index. Do not edit.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
export { AMOUNT_MASKINGS, AMOUNT_NUMERICS, UI_AMOUNT_MASKED, UiAmount, formatAmount };
|
|
114
|
+
//# sourceMappingURL=joanroucoux-cairn-ui-amount.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joanroucoux-cairn-ui-amount.mjs","sources":["../../../projects/ui/amount/format-amount.ts","../../../projects/ui/amount/amount.ts","../../../projects/ui/amount/joanroucoux-cairn-ui-amount.ts"],"sourcesContent":["const MINUS = '−';\nconst EM_DASH = '—';\nconst BULLETS = '••••';\n\nexport type AmountFormatOptions = {\n locale: string;\n currency?: string;\n signed?: boolean;\n fractionDigits?: number;\n};\n\n/** Formats a number or an amount for the locale, with a missing value as an em dash and masking behind four bullets. */\nexport function formatAmount(value: number | null | undefined, options: AmountFormatOptions, masked = false): string {\n if (value === null || value === undefined || Number.isNaN(value)) {\n return EM_DASH;\n }\n const digits = options.fractionDigits ?? 2;\n const format = new Intl.NumberFormat(options.locale, {\n ...(options.currency ? { style: 'currency', currency: options.currency } : {}),\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n });\n const magnitude = Math.abs(value);\n const parts = format.formatToParts(magnitude);\n\n if (masked) {\n let replaced = false;\n return parts\n .map((part) => {\n if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {\n if (replaced) {\n return '';\n }\n replaced = true;\n return BULLETS;\n }\n return part.value;\n })\n .join('');\n }\n\n const text = parts.map((part) => part.value).join('');\n const isZero = Number(format.format(magnitude).replace(/[^\\d]/g, '')) === 0;\n if (!options.signed || isZero) {\n return value < 0 && !isZero ? `${MINUS}${text}` : text;\n }\n return `${value > 0 ? '+' : MINUS}${text}`;\n}\n","import {\n Component,\n InjectionToken,\n LOCALE_ID,\n type Signal,\n booleanAttribute,\n computed,\n inject,\n input,\n signal,\n} from '@angular/core';\n\nimport { formatAmount } from './format-amount';\n\n/** App-wide \"amounts are hidden\" state. Defaults to never masked; the app overrides it. */\nexport const UI_AMOUNT_MASKED = new InjectionToken<Signal<boolean>>('UI_AMOUNT_MASKED', {\n providedIn: 'root',\n factory: () => signal(false),\n});\n\n/** Available figure styles. `AmountNumeric` is derived from this tuple. */\nexport const AMOUNT_NUMERICS = ['tabular', 'proportional'] as const;\nexport type AmountNumeric = (typeof AMOUNT_NUMERICS)[number];\n\n/** Available masking behaviors. `AmountMasking` is derived from this tuple. */\nexport const AMOUNT_MASKINGS = ['dots', 'hide'] as const;\nexport type AmountMasking = (typeof AMOUNT_MASKINGS)[number];\n\n/**\n * A number or an amount of money, formatted for the locale and masked when the app hides amounts.\n *\n * @example\n * <ui-amount [value]=\"total\" currency=\"EUR\" numeric=\"proportional\" />\n * <ui-amount [value]=\"dayChange\" currency=\"EUR\" signed whenMasked=\"hide\" />\n */\n@Component({\n selector: 'ui-amount',\n template: '{{ text() }}',\n host: {\n '[class]': 'classes()',\n '[attr.hidden]': 'hidden() || null',\n },\n})\nexport class UiAmount {\n readonly value = input<number | null | undefined>();\n readonly currency = input<string>();\n readonly locale = input<string>(inject(LOCALE_ID));\n readonly signed = input(false, { transform: booleanAttribute });\n readonly fractionDigits = input(2);\n readonly numeric = input<AmountNumeric>('tabular');\n readonly whenMasked = input<AmountMasking>('dots');\n\n readonly #masked = inject(UI_AMOUNT_MASKED);\n\n protected readonly hidden = computed(() => this.#masked() && this.whenMasked() === 'hide');\n\n protected readonly text = computed(() =>\n formatAmount(\n this.value(),\n {\n locale: this.locale(),\n currency: this.currency(),\n signed: this.signed(),\n fractionDigits: this.fractionDigits(),\n },\n this.#masked(),\n ),\n );\n\n protected readonly classes = computed(() => {\n const missing = this.value() === null || this.value() === undefined;\n\n return [\n 'whitespace-nowrap',\n this.numeric() === 'tabular' ? 'tabular-nums' : '',\n missing ? 'text-(--subtle-foreground)' : '',\n ]\n .filter(Boolean)\n .join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|