mn-angular-lib 1.0.167 → 1.0.168
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.
|
@@ -285,6 +285,9 @@ const mnButtonVariants = tv({
|
|
|
285
285
|
two_xl: 'rounded-2xl',
|
|
286
286
|
three_xl: 'rounded-3xl',
|
|
287
287
|
four_xl: 'rounded-4xl',
|
|
288
|
+
// A pill. Also what a fully-rounded container (e.g. a segmented track)
|
|
289
|
+
// hands its segments, since there is no tighter step below it.
|
|
290
|
+
full: 'rounded-full',
|
|
288
291
|
},
|
|
289
292
|
// Icon-button shape. `circle`/`square` drop the text padding and make the button a
|
|
290
293
|
// fixed square sized off `size` (see compoundVariants), overriding `borderRadius`.
|
|
@@ -13868,37 +13871,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
13868
13871
|
* row of loose buttons: the segments share one bordered surface, so they read as
|
|
13869
13872
|
* one control with one answer instead of several independent actions. Theme
|
|
13870
13873
|
* tokens only, so it holds up in both light and dark.
|
|
13874
|
+
*
|
|
13875
|
+
* Each segment is an `mnButton`, which owns its own size, colour, radius and
|
|
13876
|
+
* disabled look; the `segment` slot adds only what the button does not know
|
|
13877
|
+
* about — how it sits inside the track.
|
|
13871
13878
|
*/
|
|
13872
13879
|
const mnSegmentedVariants = tv({
|
|
13873
13880
|
slots: {
|
|
13874
13881
|
root: 'inline-flex items-center gap-0.5 border border-base-300 bg-base-200 p-0.5',
|
|
13875
|
-
segment: '
|
|
13876
|
-
'
|
|
13877
|
-
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ' +
|
|
13878
|
-
'disabled:opacity-50 disabled:pointer-events-none',
|
|
13882
|
+
segment: 'gap-1.5 select-none ' +
|
|
13883
|
+
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',
|
|
13879
13884
|
},
|
|
13880
13885
|
variants: {
|
|
13881
13886
|
/**
|
|
13882
|
-
* Corner rounding of the track
|
|
13883
|
-
*
|
|
13884
|
-
*
|
|
13885
|
-
* segment share the same
|
|
13887
|
+
* Corner rounding of the track. The segments are rounded one step tighter
|
|
13888
|
+
* (see `SEGMENT_RADIUS` on the component) so a filled segment nests inside
|
|
13889
|
+
* the track's own corner instead of poking out of it. Token names match
|
|
13890
|
+
* mn-button's; `full` makes a pill, where track and segment share the same
|
|
13891
|
+
* fully-rounded ends.
|
|
13886
13892
|
*/
|
|
13887
13893
|
borderRadius: {
|
|
13888
|
-
none: { root: 'rounded-none'
|
|
13889
|
-
xs: { root: 'rounded-xs'
|
|
13890
|
-
sm: { root: 'rounded-sm'
|
|
13891
|
-
md: { root: 'rounded-md'
|
|
13892
|
-
lg: { root: 'rounded-lg'
|
|
13893
|
-
xl: { root: 'rounded-xl'
|
|
13894
|
-
two_xl: { root: 'rounded-2xl'
|
|
13895
|
-
three_xl: { root: 'rounded-3xl'
|
|
13896
|
-
four_xl: { root: 'rounded-4xl'
|
|
13897
|
-
full: { root: 'rounded-full'
|
|
13898
|
-
},
|
|
13899
|
-
size: {
|
|
13900
|
-
sm: { segment: 'px-2.5 py-1 text-sm' },
|
|
13901
|
-
md: { segment: 'px-3 py-1.5 text-base' },
|
|
13894
|
+
none: { root: 'rounded-none' },
|
|
13895
|
+
xs: { root: 'rounded-xs' },
|
|
13896
|
+
sm: { root: 'rounded-sm' },
|
|
13897
|
+
md: { root: 'rounded-md' },
|
|
13898
|
+
lg: { root: 'rounded-lg' },
|
|
13899
|
+
xl: { root: 'rounded-xl' },
|
|
13900
|
+
two_xl: { root: 'rounded-2xl' },
|
|
13901
|
+
three_xl: { root: 'rounded-3xl' },
|
|
13902
|
+
four_xl: { root: 'rounded-4xl' },
|
|
13903
|
+
full: { root: 'rounded-full' },
|
|
13902
13904
|
},
|
|
13903
13905
|
/**
|
|
13904
13906
|
* Stretch the track and share its width evenly between the segments.
|
|
@@ -13913,17 +13915,17 @@ const mnSegmentedVariants = tv({
|
|
|
13913
13915
|
false: {},
|
|
13914
13916
|
},
|
|
13915
13917
|
/**
|
|
13916
|
-
*
|
|
13917
|
-
*
|
|
13918
|
-
*
|
|
13918
|
+
* Whether the segment is the picked one. The colours come from the button
|
|
13919
|
+
* itself (a filled primary button when active, a ghost one otherwise); this
|
|
13920
|
+
* only keeps an inactive segment's box the same size as an active one, whose
|
|
13921
|
+
* fill variant carries a real border.
|
|
13919
13922
|
*/
|
|
13920
13923
|
active: {
|
|
13921
|
-
true: {
|
|
13922
|
-
false: { segment: '
|
|
13924
|
+
true: {},
|
|
13925
|
+
false: { segment: 'border border-transparent' },
|
|
13923
13926
|
},
|
|
13924
13927
|
},
|
|
13925
13928
|
defaultVariants: {
|
|
13926
|
-
size: 'md',
|
|
13927
13929
|
borderRadius: 'lg',
|
|
13928
13930
|
justified: false,
|
|
13929
13931
|
active: false,
|
|
@@ -13932,6 +13934,23 @@ const mnSegmentedVariants = tv({
|
|
|
13932
13934
|
|
|
13933
13935
|
/** Icon edge length per size, so a segment's glyph matches its own text. */
|
|
13934
13936
|
const ICON_SIZE = { sm: 16, md: 18 };
|
|
13937
|
+
/**
|
|
13938
|
+
* Segment radius per track radius: one step tighter, so a filled segment nests
|
|
13939
|
+
* inside the track's corner instead of poking out of it. A pill has no tighter
|
|
13940
|
+
* step, so its segments are pills too.
|
|
13941
|
+
*/
|
|
13942
|
+
const SEGMENT_RADIUS = {
|
|
13943
|
+
none: 'none',
|
|
13944
|
+
xs: 'none',
|
|
13945
|
+
sm: 'xs',
|
|
13946
|
+
md: 'sm',
|
|
13947
|
+
lg: 'md',
|
|
13948
|
+
xl: 'lg',
|
|
13949
|
+
two_xl: 'xl',
|
|
13950
|
+
three_xl: 'two_xl',
|
|
13951
|
+
four_xl: 'three_xl',
|
|
13952
|
+
full: 'full',
|
|
13953
|
+
};
|
|
13935
13954
|
/**
|
|
13936
13955
|
* A segmented control: two or three mutually exclusive choices sharing one
|
|
13937
13956
|
* track, of which exactly one is active — a view switch (list ⇄ calendar), a
|
|
@@ -13944,6 +13963,10 @@ const ICON_SIZE = { sm: 16, md: 18 };
|
|
|
13944
13963
|
* URL, so it can sit on a page that already has a tab bar without the two
|
|
13945
13964
|
* fighting over the query string.
|
|
13946
13965
|
*
|
|
13966
|
+
* Each segment is an `mnButton` — a filled primary one when active, a ghost one
|
|
13967
|
+
* otherwise — so the control inherits the button's sizes, colours and disabled
|
|
13968
|
+
* look rather than maintaining a parallel set.
|
|
13969
|
+
*
|
|
13947
13970
|
* For switching between panes of a page, reach for `mn-tab`; this is for
|
|
13948
13971
|
* switching how one pane is rendered.
|
|
13949
13972
|
*/
|
|
@@ -13966,10 +13989,9 @@ class MnSegmented {
|
|
|
13966
13989
|
valueChange = new EventEmitter();
|
|
13967
13990
|
/** Resolves this control's own accessible names against the app's bundle. */
|
|
13968
13991
|
lang = inject(MnLanguageService);
|
|
13969
|
-
/** Resolved slot classes for the current
|
|
13992
|
+
/** Resolved slot classes for the current rounding and layout. */
|
|
13970
13993
|
get styles() {
|
|
13971
13994
|
return mnSegmentedVariants({
|
|
13972
|
-
size: this.dataSource.size,
|
|
13973
13995
|
borderRadius: this.dataSource.borderRadius,
|
|
13974
13996
|
justified: this.justified,
|
|
13975
13997
|
});
|
|
@@ -13991,17 +14013,35 @@ class MnSegmented {
|
|
|
13991
14013
|
return item.value === this.value;
|
|
13992
14014
|
}
|
|
13993
14015
|
/**
|
|
13994
|
-
*
|
|
14016
|
+
* Layout classes for one segment — how it sits in the track. Its size, colour,
|
|
14017
|
+
* radius and disabled look come from the button itself; see
|
|
14018
|
+
* {@link segmentButton}.
|
|
13995
14019
|
* @param item - The segment to style.
|
|
13996
14020
|
*/
|
|
13997
14021
|
segmentClass(item) {
|
|
13998
14022
|
return mnSegmentedVariants({
|
|
13999
|
-
size: this.dataSource.size,
|
|
14000
|
-
borderRadius: this.dataSource.borderRadius,
|
|
14001
14023
|
justified: this.justified,
|
|
14002
14024
|
active: this.isActive(item),
|
|
14003
14025
|
}).segment();
|
|
14004
14026
|
}
|
|
14027
|
+
/**
|
|
14028
|
+
* The `mnButton` configuration of one segment: a filled primary button when it
|
|
14029
|
+
* is the active choice, a ghost button otherwise, at the control's size and one
|
|
14030
|
+
* radius step inside the track. The active segment has no hover — re-picking it
|
|
14031
|
+
* does nothing, so nothing should invite the click.
|
|
14032
|
+
* @param item - The segment to configure.
|
|
14033
|
+
*/
|
|
14034
|
+
segmentButton(item) {
|
|
14035
|
+
const active = this.isActive(item);
|
|
14036
|
+
return {
|
|
14037
|
+
size: this.dataSource.size ?? 'md',
|
|
14038
|
+
variant: active ? 'fill' : 'ghost',
|
|
14039
|
+
color: active ? 'primary' : 'gray',
|
|
14040
|
+
hover: !active,
|
|
14041
|
+
borderRadius: SEGMENT_RADIUS[this.dataSource.borderRadius ?? 'lg'],
|
|
14042
|
+
disabled: item.disabled ?? false,
|
|
14043
|
+
};
|
|
14044
|
+
}
|
|
14005
14045
|
/**
|
|
14006
14046
|
* Accessible name for an icon-only segment, or null when the segment shows a
|
|
14007
14047
|
* label — that label already names it, and a second name would only compete.
|
|
@@ -14031,11 +14071,11 @@ class MnSegmented {
|
|
|
14031
14071
|
this.valueChange.emit(item.value);
|
|
14032
14072
|
}
|
|
14033
14073
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnSegmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14034
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnSegmented, isStandalone: true, selector: "mn-segmented", inputs: { dataSource: "dataSource", value: "value", justified: "justified" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<!-- One control, one answer: `role=\"group\"` with `aria-pressed` per segment, so\n the picked choice is announced as pressed rather than as a plain button\n nobody can tell the state of. Not a radiogroup \u2014 these are buttons that act\n immediately, not a value being edited inside a form. -->\n<div [attr.aria-label]=\"groupLabel\" [class]=\"styles.root()\" role=\"group\">\n @for (item of dataSource.items; track item.value) {\n <button\n (click)=\"select(item)\"\n [attr.aria-label]=\"segmentLabel(item)\"\n [attr.aria-pressed]=\"isActive(item)\"\n [class]=\"segmentClass(item)\"\n [
|
|
14074
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnSegmented, isStandalone: true, selector: "mn-segmented", inputs: { dataSource: "dataSource", value: "value", justified: "justified" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<!-- One control, one answer: `role=\"group\"` with `aria-pressed` per segment, so\n the picked choice is announced as pressed rather than as a plain button\n nobody can tell the state of. Not a radiogroup \u2014 these are buttons that act\n immediately, not a value being edited inside a form. -->\n<div [attr.aria-label]=\"groupLabel\" [class]=\"styles.root()\" role=\"group\">\n @for (item of dataSource.items; track item.value) {\n <!-- Each segment is an mn-button: the active one a filled primary button, the\n rest ghost buttons, so the control shares the button's sizes, colours and\n disabled look instead of re-deriving them. -->\n <button\n mnButton\n (click)=\"select(item)\"\n [attr.aria-label]=\"segmentLabel(item)\"\n [attr.aria-pressed]=\"isActive(item)\"\n [class]=\"segmentClass(item)\"\n [data]=\"segmentButton(item)\"\n type=\"button\"\n >\n @if (item.icon; as icon) {\n <span class=\"inline-flex shrink-0 items-center\">\n <!-- A caller's template, else lucide data rendered at the size that\n matches this control's text \u2014 the same icon convention the\n dropdown's actions use. -->\n @if (isTemplateRef(icon)) {\n <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n } @else {\n <svg [lucideIcon]=\"$any(icon)\" [size]=\"iconSize\"></svg>\n }\n </span>\n }\n @if (item.label; as label) {\n <span class=\"truncate\">{{ label | mnTranslate }}</span>\n }\n </button>\n }\n</div>\n", dependencies: [{ kind: "component", type: MnButton, selector: "button[mnButton], a[mnButton]", inputs: ["data"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "pipe", type: MnTranslatePipe, name: "mnTranslate" }] });
|
|
14035
14075
|
}
|
|
14036
14076
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnSegmented, decorators: [{
|
|
14037
14077
|
type: Component,
|
|
14038
|
-
args: [{ selector: 'mn-segmented', standalone: true, imports: [MnTranslatePipe, NgTemplateOutlet, LucideDynamicIcon], template: "<!-- One control, one answer: `role=\"group\"` with `aria-pressed` per segment, so\n the picked choice is announced as pressed rather than as a plain button\n nobody can tell the state of. Not a radiogroup \u2014 these are buttons that act\n immediately, not a value being edited inside a form. -->\n<div [attr.aria-label]=\"groupLabel\" [class]=\"styles.root()\" role=\"group\">\n @for (item of dataSource.items; track item.value) {\n <button\n (click)=\"select(item)\"\n [attr.aria-label]=\"segmentLabel(item)\"\n [attr.aria-pressed]=\"isActive(item)\"\n [class]=\"segmentClass(item)\"\n [
|
|
14078
|
+
args: [{ selector: 'mn-segmented', standalone: true, imports: [MnButton, MnTranslatePipe, NgTemplateOutlet, LucideDynamicIcon], template: "<!-- One control, one answer: `role=\"group\"` with `aria-pressed` per segment, so\n the picked choice is announced as pressed rather than as a plain button\n nobody can tell the state of. Not a radiogroup \u2014 these are buttons that act\n immediately, not a value being edited inside a form. -->\n<div [attr.aria-label]=\"groupLabel\" [class]=\"styles.root()\" role=\"group\">\n @for (item of dataSource.items; track item.value) {\n <!-- Each segment is an mn-button: the active one a filled primary button, the\n rest ghost buttons, so the control shares the button's sizes, colours and\n disabled look instead of re-deriving them. -->\n <button\n mnButton\n (click)=\"select(item)\"\n [attr.aria-label]=\"segmentLabel(item)\"\n [attr.aria-pressed]=\"isActive(item)\"\n [class]=\"segmentClass(item)\"\n [data]=\"segmentButton(item)\"\n type=\"button\"\n >\n @if (item.icon; as icon) {\n <span class=\"inline-flex shrink-0 items-center\">\n <!-- A caller's template, else lucide data rendered at the size that\n matches this control's text \u2014 the same icon convention the\n dropdown's actions use. -->\n @if (isTemplateRef(icon)) {\n <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n } @else {\n <svg [lucideIcon]=\"$any(icon)\" [size]=\"iconSize\"></svg>\n }\n </span>\n }\n @if (item.label; as label) {\n <span class=\"truncate\">{{ label | mnTranslate }}</span>\n }\n </button>\n }\n</div>\n" }]
|
|
14039
14079
|
}], propDecorators: { dataSource: [{
|
|
14040
14080
|
type: Input,
|
|
14041
14081
|
args: [{ required: true }]
|