@geosdi/ngx-ui 1.0.0 → 1.1.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/README.md +37 -3
- package/fesm2022/geosdi-ngx-ui.mjs +212 -25
- package/fesm2022/geosdi-ngx-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/ui.scss +46 -1
- package/types/geosdi-ngx-ui.d.ts +28 -0
- package/types/geosdi-ngx-ui.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -691,9 +691,12 @@ Selector: `ml-column-filter`. [Source](src/lib/table/table.component.ts).
|
|
|
691
691
|
|
|
692
692
|
Selector: `ml-tabs`. [Source](src/lib/tabs/tabs.component.ts).
|
|
693
693
|
|
|
694
|
-
| Binding
|
|
695
|
-
|
|
|
696
|
-
| `value`
|
|
694
|
+
| Binding | Kind | Type | Declaration / default |
|
|
695
|
+
| --------------------- | ----- | ------------------ | --------------------- |
|
|
696
|
+
| `value` | model | `ModelSignal<any>` | `model<any>()` |
|
|
697
|
+
| `scrollable` | input | boolean (coerced) | `false` |
|
|
698
|
+
| `previousButtonLabel` | input | string | `Previous tabs` |
|
|
699
|
+
| `nextButtonLabel` | input | string | `Next tabs` |
|
|
697
700
|
|
|
698
701
|
### TabComponent
|
|
699
702
|
|
|
@@ -780,3 +783,34 @@ manual release workflow after committing the versioned source.
|
|
|
780
783
|
- Independent CSS theme, local SVG icons and public Sass entry points.
|
|
781
784
|
- `ml-*` component selectors, `ml*` directives and `--ml-*` theme tokens.
|
|
782
785
|
- Application header offsets and map-specific stacking rules excluded from the package.
|
|
786
|
+
|
|
787
|
+
### Scrollable tabs (1.1.0)
|
|
788
|
+
|
|
789
|
+
```html
|
|
790
|
+
<ml-tabs
|
|
791
|
+
[(value)]="selectedTab"
|
|
792
|
+
[scrollable]="true"
|
|
793
|
+
previousButtonLabel="Previous regions"
|
|
794
|
+
nextButtonLabel="Next regions"
|
|
795
|
+
>
|
|
796
|
+
<ml-tablist>
|
|
797
|
+
@for (region of regions; track region.id) {
|
|
798
|
+
<ml-tab [value]="region.id">{{ region.name }}</ml-tab>
|
|
799
|
+
}
|
|
800
|
+
</ml-tablist>
|
|
801
|
+
<ml-tabpanels><!-- Matching ml-tabpanel elements --></ml-tabpanels>
|
|
802
|
+
</ml-tabs>
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
`scrollable` defaults to `false`. When enabled, tabs stay on one line, native
|
|
806
|
+
scrollbars are hidden, and previous/next buttons appear only on overflow. Buttons
|
|
807
|
+
are disabled at the corresponding end. Touch and trackpad scrolling remain
|
|
808
|
+
available. The active tab is revealed after selection, including keyboard and
|
|
809
|
+
programmatic changes. Resizing and dynamic labels recalculate overflow. Scrolling
|
|
810
|
+
respects reduced motion and right-to-left layout.
|
|
811
|
+
|
|
812
|
+
`previousButtonLabel` and `nextButtonLabel` customize the accessible button labels
|
|
813
|
+
(defaults: `Previous tabs` and `Next tabs`). Existing theme tokens style the buttons;
|
|
814
|
+
no icon package or application CSS is required. The tablist now contains an internal
|
|
815
|
+
`.ml-tablist__viewport`; custom CSS targeting direct children of `ml-tablist` should
|
|
816
|
+
instead target `.ml-tablist__viewport > ml-tab`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, Injectable, ViewContainerRef, afterNextRender, booleanAttribute, computed, contentChild, effect, forwardRef, inject, input, linkedSignal, model, numberAttribute, output, signal, untracked, viewChild } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, Injectable, ViewContainerRef, afterNextRender, afterRenderEffect, booleanAttribute, computed, contentChild, effect, forwardRef, inject, input, linkedSignal, model, numberAttribute, output, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|
4
4
|
import * as i1 from "@angular/forms";
|
|
5
5
|
import { FormsModule, NG_VALUE_ACCESSOR, NgControl } from "@angular/forms";
|
|
@@ -4695,6 +4695,12 @@ let nextId$4 = 0;
|
|
|
4695
4695
|
var TabsComponent = class TabsComponent {
|
|
4696
4696
|
constructor() {
|
|
4697
4697
|
this.value = model(...ngDevMode ? [void 0, { debugName: "value" }] : /* istanbul ignore next */ []);
|
|
4698
|
+
this.scrollable = input(false, {
|
|
4699
|
+
...ngDevMode ? { debugName: "scrollable" } : /* istanbul ignore next */ {},
|
|
4700
|
+
transform: booleanAttribute
|
|
4701
|
+
});
|
|
4702
|
+
this.previousButtonLabel = input("Previous tabs", ...ngDevMode ? [{ debugName: "previousButtonLabel" }] : /* istanbul ignore next */ []);
|
|
4703
|
+
this.nextButtonLabel = input("Next tabs", ...ngDevMode ? [{ debugName: "nextButtonLabel" }] : /* istanbul ignore next */ []);
|
|
4698
4704
|
this.uid = `ml-tabs-${nextId$4++}`;
|
|
4699
4705
|
}
|
|
4700
4706
|
isActive(value) {
|
|
@@ -4720,13 +4726,36 @@ var TabsComponent = class TabsComponent {
|
|
|
4720
4726
|
type: TabsComponent,
|
|
4721
4727
|
isStandalone: true,
|
|
4722
4728
|
selector: "ml-tabs",
|
|
4723
|
-
inputs: {
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4729
|
+
inputs: {
|
|
4730
|
+
value: {
|
|
4731
|
+
classPropertyName: "value",
|
|
4732
|
+
publicName: "value",
|
|
4733
|
+
isSignal: true,
|
|
4734
|
+
isRequired: false,
|
|
4735
|
+
transformFunction: null
|
|
4736
|
+
},
|
|
4737
|
+
scrollable: {
|
|
4738
|
+
classPropertyName: "scrollable",
|
|
4739
|
+
publicName: "scrollable",
|
|
4740
|
+
isSignal: true,
|
|
4741
|
+
isRequired: false,
|
|
4742
|
+
transformFunction: null
|
|
4743
|
+
},
|
|
4744
|
+
previousButtonLabel: {
|
|
4745
|
+
classPropertyName: "previousButtonLabel",
|
|
4746
|
+
publicName: "previousButtonLabel",
|
|
4747
|
+
isSignal: true,
|
|
4748
|
+
isRequired: false,
|
|
4749
|
+
transformFunction: null
|
|
4750
|
+
},
|
|
4751
|
+
nextButtonLabel: {
|
|
4752
|
+
classPropertyName: "nextButtonLabel",
|
|
4753
|
+
publicName: "nextButtonLabel",
|
|
4754
|
+
isSignal: true,
|
|
4755
|
+
isRequired: false,
|
|
4756
|
+
transformFunction: null
|
|
4757
|
+
}
|
|
4758
|
+
},
|
|
4730
4759
|
outputs: { value: "valueChange" },
|
|
4731
4760
|
host: { classAttribute: "ml-tabs" },
|
|
4732
4761
|
ngImport: i0,
|
|
@@ -4750,24 +4779,100 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
4750
4779
|
template: `<ng-content />`
|
|
4751
4780
|
}]
|
|
4752
4781
|
}],
|
|
4753
|
-
propDecorators: {
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4782
|
+
propDecorators: {
|
|
4783
|
+
value: [{
|
|
4784
|
+
type: i0.Input,
|
|
4785
|
+
args: [{
|
|
4786
|
+
isSignal: true,
|
|
4787
|
+
alias: "value",
|
|
4788
|
+
required: false
|
|
4789
|
+
}]
|
|
4790
|
+
}, {
|
|
4791
|
+
type: i0.Output,
|
|
4792
|
+
args: ["valueChange"]
|
|
4793
|
+
}],
|
|
4794
|
+
scrollable: [{
|
|
4795
|
+
type: i0.Input,
|
|
4796
|
+
args: [{
|
|
4797
|
+
isSignal: true,
|
|
4798
|
+
alias: "scrollable",
|
|
4799
|
+
required: false
|
|
4800
|
+
}]
|
|
4801
|
+
}],
|
|
4802
|
+
previousButtonLabel: [{
|
|
4803
|
+
type: i0.Input,
|
|
4804
|
+
args: [{
|
|
4805
|
+
isSignal: true,
|
|
4806
|
+
alias: "previousButtonLabel",
|
|
4807
|
+
required: false
|
|
4808
|
+
}]
|
|
4809
|
+
}],
|
|
4810
|
+
nextButtonLabel: [{
|
|
4811
|
+
type: i0.Input,
|
|
4812
|
+
args: [{
|
|
4813
|
+
isSignal: true,
|
|
4814
|
+
alias: "nextButtonLabel",
|
|
4815
|
+
required: false
|
|
4816
|
+
}]
|
|
4759
4817
|
}]
|
|
4760
|
-
}
|
|
4761
|
-
type: i0.Output,
|
|
4762
|
-
args: ["valueChange"]
|
|
4763
|
-
}] }
|
|
4818
|
+
}
|
|
4764
4819
|
});
|
|
4765
4820
|
var TabListComponent = class TabListComponent {
|
|
4766
4821
|
constructor() {
|
|
4767
4822
|
this.host = inject(ElementRef);
|
|
4823
|
+
this.tabs = inject(TabsComponent);
|
|
4824
|
+
this.overflowing = signal(false, ...ngDevMode ? [{ debugName: "overflowing" }] : /* istanbul ignore next */ []);
|
|
4825
|
+
this.atStart = signal(true, ...ngDevMode ? [{ debugName: "atStart" }] : /* istanbul ignore next */ []);
|
|
4826
|
+
this.atEnd = signal(false, ...ngDevMode ? [{ debugName: "atEnd" }] : /* istanbul ignore next */ []);
|
|
4827
|
+
this.viewport = viewChild.required("viewport", ...ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []);
|
|
4828
|
+
const destroyRef = inject(DestroyRef);
|
|
4829
|
+
afterNextRender(() => {
|
|
4830
|
+
const resize = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(() => this.measure());
|
|
4831
|
+
resize?.observe(this.viewport().nativeElement);
|
|
4832
|
+
const changes = new MutationObserver(() => this.measure());
|
|
4833
|
+
changes.observe(this.viewport().nativeElement, {
|
|
4834
|
+
childList: true,
|
|
4835
|
+
subtree: true,
|
|
4836
|
+
characterData: true
|
|
4837
|
+
});
|
|
4838
|
+
destroyRef.onDestroy(() => {
|
|
4839
|
+
resize?.disconnect();
|
|
4840
|
+
changes.disconnect();
|
|
4841
|
+
});
|
|
4842
|
+
this.measure();
|
|
4843
|
+
});
|
|
4844
|
+
afterRenderEffect(() => {
|
|
4845
|
+
this.tabs.value();
|
|
4846
|
+
if (this.tabs.scrollable()) this.revealActive();
|
|
4847
|
+
this.measure();
|
|
4848
|
+
});
|
|
4849
|
+
}
|
|
4850
|
+
measure() {
|
|
4851
|
+
const el = this.viewport().nativeElement;
|
|
4852
|
+
this.overflowing.set(el.scrollWidth > el.clientWidth + 1);
|
|
4853
|
+
const offset = Math.abs(el.scrollLeft);
|
|
4854
|
+
this.atStart.set(offset <= 1);
|
|
4855
|
+
this.atEnd.set(offset + el.clientWidth >= el.scrollWidth - 1);
|
|
4856
|
+
}
|
|
4857
|
+
scroll(direction) {
|
|
4858
|
+
const el = this.viewport().nativeElement;
|
|
4859
|
+
const rtl = getComputedStyle(el).direction === "rtl";
|
|
4860
|
+
el.scrollBy({
|
|
4861
|
+
left: direction * (rtl ? -1 : 1) * el.clientWidth * .75,
|
|
4862
|
+
behavior: typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
|
|
4863
|
+
});
|
|
4864
|
+
}
|
|
4865
|
+
revealActive() {
|
|
4866
|
+
const el = this.viewport().nativeElement;
|
|
4867
|
+
const active = el.querySelector("[role=\"tab\"][aria-selected=\"true\"]");
|
|
4868
|
+
if (!active) return;
|
|
4869
|
+
const bounds = el.getBoundingClientRect();
|
|
4870
|
+
const tab = active.getBoundingClientRect();
|
|
4871
|
+
if (tab.left < bounds.left) el.scrollLeft += tab.left - bounds.left;
|
|
4872
|
+
else if (tab.right > bounds.right) el.scrollLeft += tab.right - bounds.right;
|
|
4768
4873
|
}
|
|
4769
4874
|
keydown(event) {
|
|
4770
|
-
const tabs = Array.from(this.host.nativeElement.querySelectorAll("
|
|
4875
|
+
const tabs = Array.from(this.host.nativeElement.querySelectorAll("[role=\"tab\"]"));
|
|
4771
4876
|
const at = tabs.indexOf(document.activeElement);
|
|
4772
4877
|
if (at < 0) return;
|
|
4773
4878
|
const to = {
|
|
@@ -4794,18 +4899,60 @@ var TabListComponent = class TabListComponent {
|
|
|
4794
4899
|
}
|
|
4795
4900
|
static {
|
|
4796
4901
|
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
4797
|
-
minVersion: "
|
|
4902
|
+
minVersion: "17.0.0",
|
|
4798
4903
|
version: "22.2.4",
|
|
4799
4904
|
type: TabListComponent,
|
|
4800
4905
|
isStandalone: true,
|
|
4801
4906
|
selector: "ml-tablist",
|
|
4802
4907
|
host: {
|
|
4803
|
-
attributes: { "role": "tablist" },
|
|
4804
4908
|
listeners: { "keydown": "keydown($event)" },
|
|
4909
|
+
properties: { "class.ml-tablist--scrollable": "tabs.scrollable()" },
|
|
4805
4910
|
classAttribute: "ml-tablist"
|
|
4806
4911
|
},
|
|
4912
|
+
viewQueries: [{
|
|
4913
|
+
propertyName: "viewport",
|
|
4914
|
+
first: true,
|
|
4915
|
+
predicate: ["viewport"],
|
|
4916
|
+
descendants: true,
|
|
4917
|
+
isSignal: true
|
|
4918
|
+
}],
|
|
4807
4919
|
ngImport: i0,
|
|
4808
|
-
template:
|
|
4920
|
+
template: `
|
|
4921
|
+
@if (tabs.scrollable() && overflowing()) {
|
|
4922
|
+
<button
|
|
4923
|
+
type="button"
|
|
4924
|
+
class="ml-tablist__scroll-button"
|
|
4925
|
+
[attr.aria-label]="tabs.previousButtonLabel()"
|
|
4926
|
+
[disabled]="atStart()"
|
|
4927
|
+
(click)="scroll(-1)"
|
|
4928
|
+
>
|
|
4929
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
4930
|
+
<path d="m14 6-6 6 6 6" />
|
|
4931
|
+
</svg>
|
|
4932
|
+
</button>
|
|
4933
|
+
}
|
|
4934
|
+
<div
|
|
4935
|
+
#viewport
|
|
4936
|
+
class="ml-tablist__viewport"
|
|
4937
|
+
role="tablist"
|
|
4938
|
+
(scroll)="measure()"
|
|
4939
|
+
>
|
|
4940
|
+
<ng-content />
|
|
4941
|
+
</div>
|
|
4942
|
+
@if (tabs.scrollable() && overflowing()) {
|
|
4943
|
+
<button
|
|
4944
|
+
type="button"
|
|
4945
|
+
class="ml-tablist__scroll-button"
|
|
4946
|
+
[attr.aria-label]="tabs.nextButtonLabel()"
|
|
4947
|
+
[disabled]="atEnd()"
|
|
4948
|
+
(click)="scroll(1)"
|
|
4949
|
+
>
|
|
4950
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
4951
|
+
<path d="m10 6 6 6-6 6" />
|
|
4952
|
+
</svg>
|
|
4953
|
+
</button>
|
|
4954
|
+
}
|
|
4955
|
+
`,
|
|
4809
4956
|
isInline: true,
|
|
4810
4957
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
4811
4958
|
});
|
|
@@ -4823,12 +4970,52 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
4823
4970
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4824
4971
|
host: {
|
|
4825
4972
|
class: "ml-tablist",
|
|
4826
|
-
|
|
4973
|
+
"[class.ml-tablist--scrollable]": "tabs.scrollable()",
|
|
4827
4974
|
"(keydown)": "keydown($event)"
|
|
4828
4975
|
},
|
|
4829
|
-
template:
|
|
4976
|
+
template: `
|
|
4977
|
+
@if (tabs.scrollable() && overflowing()) {
|
|
4978
|
+
<button
|
|
4979
|
+
type="button"
|
|
4980
|
+
class="ml-tablist__scroll-button"
|
|
4981
|
+
[attr.aria-label]="tabs.previousButtonLabel()"
|
|
4982
|
+
[disabled]="atStart()"
|
|
4983
|
+
(click)="scroll(-1)"
|
|
4984
|
+
>
|
|
4985
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
4986
|
+
<path d="m14 6-6 6 6 6" />
|
|
4987
|
+
</svg>
|
|
4988
|
+
</button>
|
|
4989
|
+
}
|
|
4990
|
+
<div
|
|
4991
|
+
#viewport
|
|
4992
|
+
class="ml-tablist__viewport"
|
|
4993
|
+
role="tablist"
|
|
4994
|
+
(scroll)="measure()"
|
|
4995
|
+
>
|
|
4996
|
+
<ng-content />
|
|
4997
|
+
</div>
|
|
4998
|
+
@if (tabs.scrollable() && overflowing()) {
|
|
4999
|
+
<button
|
|
5000
|
+
type="button"
|
|
5001
|
+
class="ml-tablist__scroll-button"
|
|
5002
|
+
[attr.aria-label]="tabs.nextButtonLabel()"
|
|
5003
|
+
[disabled]="atEnd()"
|
|
5004
|
+
(click)="scroll(1)"
|
|
5005
|
+
>
|
|
5006
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
5007
|
+
<path d="m10 6 6 6-6 6" />
|
|
5008
|
+
</svg>
|
|
5009
|
+
</button>
|
|
5010
|
+
}
|
|
5011
|
+
`
|
|
4830
5012
|
}]
|
|
4831
|
-
}]
|
|
5013
|
+
}],
|
|
5014
|
+
ctorParameters: () => [],
|
|
5015
|
+
propDecorators: { viewport: [{
|
|
5016
|
+
type: i0.ViewChild,
|
|
5017
|
+
args: ["viewport", { isSignal: true }]
|
|
5018
|
+
}] }
|
|
4832
5019
|
});
|
|
4833
5020
|
var TabComponent = class TabComponent {
|
|
4834
5021
|
constructor() {
|