@akcelik/strct 0.2.0 → 0.3.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 +11 -1
- package/fesm2022/akcelik-strct.mjs +188 -17
- package/fesm2022/akcelik-strct.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_fonts.scss +49 -0
- package/styles/fonts/dm-sans-400.woff2 +0 -0
- package/styles/fonts/dm-sans-500.woff2 +0 -0
- package/styles/fonts/dm-sans-600.woff2 +0 -0
- package/styles/fonts/dm-sans-700.woff2 +0 -0
- package/styles/fonts/jetbrains-mono-400.woff2 +0 -0
- package/styles/fonts/jetbrains-mono-500.woff2 +0 -0
- package/styles/theme.scss +1 -0
- package/types/akcelik-strct.d.ts +39 -3
package/README.md
CHANGED
|
@@ -19,7 +19,13 @@ npm install @akcelik/strct
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
`@angular/core`, `@angular/common`, `@angular/forms` and
|
|
22
|
-
`@angular/platform-browser` are peer dependencies.
|
|
22
|
+
`@angular/platform-browser` are peer dependencies (Angular 21.2+).
|
|
23
|
+
|
|
24
|
+
> **Consuming a local build?** Install the packed tarball
|
|
25
|
+
> (`ng build strct && npm pack dist/strct`, then `npm i ../akcelik-strct-x.y.z.tgz`)
|
|
26
|
+
> rather than a `file:` symlink — a symlinked dependency resolves its own copy of
|
|
27
|
+
> `@angular/core`, which can trip an `InputSignal` brand mismatch (TS2551) across
|
|
28
|
+
> Angular patch versions.
|
|
23
29
|
|
|
24
30
|
## Theme setup
|
|
25
31
|
|
|
@@ -31,6 +37,10 @@ form-control styles):
|
|
|
31
37
|
@use '@akcelik/strct/styles/theme';
|
|
32
38
|
```
|
|
33
39
|
|
|
40
|
+
The theme **self-hosts its fonts** (DM Sans + JetBrains Mono, OFL) — they ship as
|
|
41
|
+
`woff2` under `styles/fonts/` and are referenced by `@font-face`, so there is no
|
|
42
|
+
external request and nothing else to load.
|
|
43
|
+
|
|
34
44
|
Set the scheme on the document root (or let `StrctThemeService` manage it):
|
|
35
45
|
|
|
36
46
|
```html
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, booleanAttribute, output, HostListener, model, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, TemplateRef, contentChild, Renderer2 } from '@angular/core';
|
|
2
|
+
import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, booleanAttribute, output, HostListener, model, contentChildren, effect, DestroyRef, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, TemplateRef, contentChild, Renderer2 } from '@angular/core';
|
|
3
3
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
4
|
import { DOCUMENT as DOCUMENT$1, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
@@ -124,6 +124,9 @@ const STRCT_ICONS = {
|
|
|
124
124
|
code: '<path d="M5.5 5L2.5 8l3 3M10.5 5l3 3-3 3M9 3.5l-2 9"/>',
|
|
125
125
|
book: '<path d="M3 3.2h6a1.5 1.5 0 0 1 1.5 1.5v8.1H4.5A1.5 1.5 0 0 1 3 11.3z"/><path d="M13 3.2H9.5A1.5 1.5 0 0 0 8 4.7v8.1h5z"/>',
|
|
126
126
|
terminal: '<rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M4.8 6.5L7 8.2 4.8 9.9M8.2 10.2h3"/>',
|
|
127
|
+
folder: '<path d="M2 4.6a1 1 0 0 1 1-1h3.1l1.3 1.6H13a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1z"/>',
|
|
128
|
+
template: '<path d="M4.3 2.5h4.6l3.1 3.1V13a.5.5 0 0 1-.5.5H4.3a.5.5 0 0 1-.5-.5V3a.5.5 0 0 1 .5-.5z"/><path d="M8.7 2.6v3.1h3.1"/><path d="M5.8 9h4.2M5.8 11h2.6"/>',
|
|
129
|
+
tag: '<path d="M2.6 7.7V3.2a.6.6 0 0 1 .6-.6h4.5l5.6 5.6a1 1 0 0 1 0 1.4l-3.5 3.5a1 1 0 0 1-1.4 0z"/><circle cx="5.4" cy="5.4" r=".9" fill="currentColor" stroke="none"/>',
|
|
127
130
|
// ── Datacenter / infrastructure ─────────────────────────────
|
|
128
131
|
datacenter: '<rect x="2.5" y="2.5" width="11" height="11" rx="1"/><path d="M5 5h6M5 7.3h6M5 9.6h3.5"/><circle cx="11" cy="9.7" r=".5" fill="currentColor" stroke="none"/>',
|
|
129
132
|
rack: '<rect x="3.5" y="2" width="9" height="12" rx="1"/><path d="M3.5 5.2h9M3.5 8.4h9M3.5 11.6h9"/><circle cx="5.4" cy="3.6" r=".4" fill="currentColor" stroke="none"/><circle cx="5.4" cy="6.8" r=".4" fill="currentColor" stroke="none"/><circle cx="5.4" cy="10" r=".4" fill="currentColor" stroke="none"/>',
|
|
@@ -144,6 +147,10 @@ const STRCT_ICONS = {
|
|
|
144
147
|
hba: '<rect x="2.3" y="3.4" width="11.4" height="6.4" rx="1"/><circle cx="5.1" cy="6.6" r="1.2"/><circle cx="8.1" cy="6.6" r="1.2"/><path d="M10.6 5.6h1.6M10.6 7.6h1.6M5 9.8v2.6M11 9.8v2.6"/>',
|
|
145
148
|
// RJ45 ethernet port (switch / server) — keyed jack with contact pins.
|
|
146
149
|
ethernet: '<rect x="2.5" y="4" width="11" height="7.4" rx="1"/><path d="M6 4V2.6h4V4"/><path d="M5.3 7.6v2M7.1 7.6v2M8.9 7.6v2M10.7 7.6v2"/>',
|
|
150
|
+
// Resource pool — a proportional allocation (pie with two radii).
|
|
151
|
+
resourcePool: '<circle cx="8" cy="8" r="5.6"/><path d="M8 8V2.4M8 8l4.9 2.7"/>',
|
|
152
|
+
// Port group — a grouped set of switch ports under a shared rail.
|
|
153
|
+
portGroup: '<path d="M1.8 4.4h12.4"/><rect x="2.2" y="6.2" width="3.4" height="4" rx=".5"/><rect x="6.3" y="6.2" width="3.4" height="4" rx=".5"/><rect x="10.4" y="6.2" width="3.4" height="4" rx=".5"/><path d="M3.9 10.2v1.6M8 10.2v1.6M12.1 10.2v1.6"/>',
|
|
147
154
|
// ── Accessibility (original glyphs for generic concepts) ─────
|
|
148
155
|
universalAccess: '<circle cx="8" cy="8" r="6"/><circle cx="8" cy="4.9" r=".9" fill="currentColor" stroke="none"/><path d="M4.9 6.3c2 .8 4.2 .8 6.2 0M8 6.4v3.1M6.3 11.9 8 9.4l1.7 2.5"/>',
|
|
149
156
|
wheelchair: '<circle cx="6.5" cy="3.1" r="1.3"/><path d="M6.5 4.5v3.3h3.1l1.7 3.4"/><circle cx="6.7" cy="11.3" r="2.6"/><path d="M9.3 11.3h2.1l-.5 1.6"/>',
|
|
@@ -181,7 +188,7 @@ const STRCT_ICON_GROUPS = [
|
|
|
181
188
|
'hexagon', 'search', 'menu', 'ellipsis', 'dots', 'close', 'check', 'calendar',
|
|
182
189
|
'eye', 'eyeOff', 'upload', 'download', 'sun', 'moon', 'bell', 'heart', 'layers',
|
|
183
190
|
'grid', 'form', 'chart', 'bars', 'gauge', 'palette', 'sidebar', 'compass',
|
|
184
|
-
'copy', 'code', 'book', 'terminal',
|
|
191
|
+
'copy', 'code', 'book', 'terminal', 'folder', 'template', 'tag',
|
|
185
192
|
],
|
|
186
193
|
},
|
|
187
194
|
{
|
|
@@ -201,6 +208,7 @@ const STRCT_ICON_GROUPS = [
|
|
|
201
208
|
names: [
|
|
202
209
|
'datacenter', 'rack', 'cluster', 'host', 'vm', 'switch', 'storage',
|
|
203
210
|
'network', 'cpu', 'memory', 'disk', 'port', 'nic', 'hba', 'ethernet', 'power',
|
|
211
|
+
'resourcePool', 'portGroup',
|
|
204
212
|
],
|
|
205
213
|
},
|
|
206
214
|
{
|
|
@@ -436,10 +444,16 @@ class StrctOverlay {
|
|
|
436
444
|
let left;
|
|
437
445
|
if (p === 'right') {
|
|
438
446
|
left = a.right + gap;
|
|
447
|
+
// Flip to the left of the anchor if it would overflow the right edge.
|
|
448
|
+
if (left + w > vw - margin && a.left - gap - w > margin)
|
|
449
|
+
left = a.left - gap - w;
|
|
439
450
|
top = a.top;
|
|
440
451
|
}
|
|
441
452
|
else if (p === 'left') {
|
|
442
453
|
left = a.left - gap - w;
|
|
454
|
+
// Flip to the right of the anchor if it would overflow the left edge.
|
|
455
|
+
if (left < margin && a.right + gap + w < vw - margin)
|
|
456
|
+
left = a.right + gap;
|
|
443
457
|
top = a.top;
|
|
444
458
|
}
|
|
445
459
|
else {
|
|
@@ -1295,6 +1309,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
1295
1309
|
}], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], nodeActivated: [{ type: i0.Output, args: ["nodeActivated"] }] } });
|
|
1296
1310
|
|
|
1297
1311
|
let modalCounter = 0;
|
|
1312
|
+
// Body scroll-lock shared across any number of simultaneously open modals.
|
|
1313
|
+
let scrollLockCount = 0;
|
|
1314
|
+
let savedBodyOverflow = '';
|
|
1315
|
+
function lockBodyScroll(doc) {
|
|
1316
|
+
if (scrollLockCount === 0) {
|
|
1317
|
+
savedBodyOverflow = doc.body.style.overflow;
|
|
1318
|
+
doc.body.style.overflow = 'hidden';
|
|
1319
|
+
}
|
|
1320
|
+
scrollLockCount++;
|
|
1321
|
+
}
|
|
1322
|
+
function unlockBodyScroll(doc) {
|
|
1323
|
+
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
1324
|
+
if (scrollLockCount === 0)
|
|
1325
|
+
doc.body.style.overflow = savedBodyOverflow;
|
|
1326
|
+
}
|
|
1298
1327
|
/**
|
|
1299
1328
|
* Overlay dialog with two-way `open`:
|
|
1300
1329
|
* <strct-modal [(open)]="show" title="Confirm">
|
|
@@ -1317,16 +1346,32 @@ class StrctModal {
|
|
|
1317
1346
|
titleId = `strct-modal-${++modalCounter}`;
|
|
1318
1347
|
/** Element that had focus before the dialog opened, restored on close. */
|
|
1319
1348
|
previousActive = null;
|
|
1349
|
+
/** Whether this instance currently holds a scroll lock. */
|
|
1350
|
+
locked = false;
|
|
1320
1351
|
constructor() {
|
|
1321
1352
|
effect(() => {
|
|
1322
|
-
|
|
1353
|
+
const open = this.open();
|
|
1354
|
+
if (open && !this.locked) {
|
|
1355
|
+
this.locked = true;
|
|
1356
|
+
lockBodyScroll(this.doc);
|
|
1323
1357
|
this.previousActive = this.doc.activeElement;
|
|
1324
1358
|
// Move focus into the dialog once it has rendered.
|
|
1325
1359
|
setTimeout(() => this.focusInitial());
|
|
1326
1360
|
}
|
|
1327
|
-
else if (this.
|
|
1328
|
-
this.
|
|
1329
|
-
this.
|
|
1361
|
+
else if (!open && this.locked) {
|
|
1362
|
+
this.locked = false;
|
|
1363
|
+
unlockBodyScroll(this.doc);
|
|
1364
|
+
if (this.previousActive) {
|
|
1365
|
+
this.previousActive.focus?.();
|
|
1366
|
+
this.previousActive = null;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
// Release the lock if the modal is destroyed while still open.
|
|
1371
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1372
|
+
if (this.locked) {
|
|
1373
|
+
this.locked = false;
|
|
1374
|
+
unlockBodyScroll(this.doc);
|
|
1330
1375
|
}
|
|
1331
1376
|
});
|
|
1332
1377
|
}
|
|
@@ -1627,28 +1672,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
1627
1672
|
}] } });
|
|
1628
1673
|
|
|
1629
1674
|
/**
|
|
1630
|
-
* A nested fly-out inside a `strct-context-menu` or `strct-dropdown`.
|
|
1631
|
-
*
|
|
1675
|
+
* A nested fly-out inside a `strct-context-menu` or `strct-dropdown`. Opens on
|
|
1676
|
+
* hover, click/tap, or the keyboard (Enter / Space / →), and flips to the left
|
|
1677
|
+
* near the right edge of the viewport. Reuse `strct-dropdown-item` for entries.
|
|
1632
1678
|
* <strct-submenu label="Power">
|
|
1633
1679
|
* <strct-dropdown-item>Power on</strct-dropdown-item>
|
|
1634
1680
|
* <strct-dropdown-item>Power off</strct-dropdown-item>
|
|
1635
1681
|
* </strct-submenu>
|
|
1636
1682
|
*/
|
|
1637
1683
|
class StrctSubmenu {
|
|
1684
|
+
host = inject(ElementRef);
|
|
1638
1685
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1639
1686
|
/** Optional leading icon; when omitted the icon column is still reserved so
|
|
1640
1687
|
* the label stays aligned with sibling items that do have icons. */
|
|
1641
1688
|
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
1642
1689
|
open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
1690
|
+
/** Open to the left when the fly-out would overflow the right edge. */
|
|
1691
|
+
flip = signal(false, ...(ngDevMode ? [{ debugName: "flip" }] : /* istanbul ignore next */ []));
|
|
1692
|
+
setOpen(value) {
|
|
1693
|
+
if (value) {
|
|
1694
|
+
const rect = this.host.nativeElement.getBoundingClientRect();
|
|
1695
|
+
this.flip.set(rect.right + 190 > window.innerWidth);
|
|
1696
|
+
}
|
|
1697
|
+
this.open.set(value);
|
|
1698
|
+
}
|
|
1643
1699
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctSubmenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1644
1700
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctSubmenu, isStandalone: true, selector: "strct-submenu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-submenu-host" }, ngImport: i0, template: `
|
|
1645
|
-
<div class="strct-submenu" (mouseenter)="
|
|
1701
|
+
<div class="strct-submenu" (mouseenter)="setOpen(true)" (mouseleave)="open.set(false)">
|
|
1646
1702
|
<div
|
|
1647
1703
|
class="strct-submenu__trigger"
|
|
1648
1704
|
role="menuitem"
|
|
1705
|
+
tabindex="0"
|
|
1649
1706
|
aria-haspopup="menu"
|
|
1650
1707
|
[attr.aria-expanded]="open()"
|
|
1651
|
-
(click)="$event.stopPropagation()"
|
|
1708
|
+
(click)="$event.stopPropagation(); setOpen(!open())"
|
|
1709
|
+
(keydown.enter)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1710
|
+
(keydown.space)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1711
|
+
(keydown.arrowright)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1712
|
+
(keydown.arrowleft)="$event.stopPropagation(); open.set(false)"
|
|
1713
|
+
(keydown.escape)="$event.stopPropagation(); open.set(false)"
|
|
1652
1714
|
>
|
|
1653
1715
|
@if (icon()) {
|
|
1654
1716
|
<strct-icon class="strct-submenu__icon" [name]="icon()" [size]="14" [strokeWidth]="1.3" />
|
|
@@ -1659,21 +1721,29 @@ class StrctSubmenu {
|
|
|
1659
1721
|
<strct-icon class="strct-submenu__arrow" name="chevronRight" [size]="12" [strokeWidth]="1.6" />
|
|
1660
1722
|
</div>
|
|
1661
1723
|
@if (open()) {
|
|
1662
|
-
<div class="strct-submenu__panel" role="menu"
|
|
1724
|
+
<div class="strct-submenu__panel" [class.strct-submenu__panel--flip]="flip()" role="menu">
|
|
1725
|
+
<ng-content />
|
|
1726
|
+
</div>
|
|
1663
1727
|
}
|
|
1664
1728
|
</div>
|
|
1665
|
-
`, isInline: true, styles: [".strct-submenu{position:relative}.strct-submenu__trigger{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 10px;border-radius:5px;cursor:default;font-size:13px;color:var(--t1)}.strct-submenu__trigger:hover{background:var(--bg-3)}.strct-submenu__icon{color:var(--t2);flex-shrink:0}.strct-submenu__icon-spacer{width:14px;flex-shrink:0}.strct-submenu__label{flex:1;display:inline-flex;align-items:center;gap:8px}.strct-submenu__arrow{color:var(--t3)}.strct-submenu__panel{position:absolute;top:-5px;left:100%;z-index:1;min-width:170px;margin-left:2px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-submenu-in .1s ease}@keyframes strct-submenu-in{0%{opacity:0;transform:translate(-4px)}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1729
|
+
`, isInline: true, styles: [".strct-submenu{position:relative}.strct-submenu__trigger{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 10px;border-radius:5px;cursor:default;font-size:13px;color:var(--t1)}.strct-submenu__trigger:hover{background:var(--bg-3)}.strct-submenu__trigger:focus-visible{outline:none;background:var(--bg-3)}.strct-submenu__icon{color:var(--t2);flex-shrink:0}.strct-submenu__icon-spacer{width:14px;flex-shrink:0}.strct-submenu__label{flex:1;display:inline-flex;align-items:center;gap:8px}.strct-submenu__arrow{color:var(--t3)}.strct-submenu__panel{position:absolute;top:-5px;left:100%;z-index:1;min-width:170px;margin-left:2px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-submenu-in .1s ease}.strct-submenu__panel--flip{left:auto;right:100%;margin-left:0;margin-right:2px}@keyframes strct-submenu-in{0%{opacity:0;transform:translate(-4px)}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1666
1730
|
}
|
|
1667
1731
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctSubmenu, decorators: [{
|
|
1668
1732
|
type: Component,
|
|
1669
1733
|
args: [{ selector: 'strct-submenu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
|
|
1670
|
-
<div class="strct-submenu" (mouseenter)="
|
|
1734
|
+
<div class="strct-submenu" (mouseenter)="setOpen(true)" (mouseleave)="open.set(false)">
|
|
1671
1735
|
<div
|
|
1672
1736
|
class="strct-submenu__trigger"
|
|
1673
1737
|
role="menuitem"
|
|
1738
|
+
tabindex="0"
|
|
1674
1739
|
aria-haspopup="menu"
|
|
1675
1740
|
[attr.aria-expanded]="open()"
|
|
1676
|
-
(click)="$event.stopPropagation()"
|
|
1741
|
+
(click)="$event.stopPropagation(); setOpen(!open())"
|
|
1742
|
+
(keydown.enter)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1743
|
+
(keydown.space)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1744
|
+
(keydown.arrowright)="$event.preventDefault(); $event.stopPropagation(); setOpen(true)"
|
|
1745
|
+
(keydown.arrowleft)="$event.stopPropagation(); open.set(false)"
|
|
1746
|
+
(keydown.escape)="$event.stopPropagation(); open.set(false)"
|
|
1677
1747
|
>
|
|
1678
1748
|
@if (icon()) {
|
|
1679
1749
|
<strct-icon class="strct-submenu__icon" [name]="icon()" [size]="14" [strokeWidth]="1.3" />
|
|
@@ -1684,10 +1754,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
1684
1754
|
<strct-icon class="strct-submenu__arrow" name="chevronRight" [size]="12" [strokeWidth]="1.6" />
|
|
1685
1755
|
</div>
|
|
1686
1756
|
@if (open()) {
|
|
1687
|
-
<div class="strct-submenu__panel" role="menu"
|
|
1757
|
+
<div class="strct-submenu__panel" [class.strct-submenu__panel--flip]="flip()" role="menu">
|
|
1758
|
+
<ng-content />
|
|
1759
|
+
</div>
|
|
1688
1760
|
}
|
|
1689
1761
|
</div>
|
|
1690
|
-
`, host: { class: 'strct-submenu-host' }, styles: [".strct-submenu{position:relative}.strct-submenu__trigger{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 10px;border-radius:5px;cursor:default;font-size:13px;color:var(--t1)}.strct-submenu__trigger:hover{background:var(--bg-3)}.strct-submenu__icon{color:var(--t2);flex-shrink:0}.strct-submenu__icon-spacer{width:14px;flex-shrink:0}.strct-submenu__label{flex:1;display:inline-flex;align-items:center;gap:8px}.strct-submenu__arrow{color:var(--t3)}.strct-submenu__panel{position:absolute;top:-5px;left:100%;z-index:1;min-width:170px;margin-left:2px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-submenu-in .1s ease}@keyframes strct-submenu-in{0%{opacity:0;transform:translate(-4px)}}\n"] }]
|
|
1762
|
+
`, host: { class: 'strct-submenu-host' }, styles: [".strct-submenu{position:relative}.strct-submenu__trigger{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 10px;border-radius:5px;cursor:default;font-size:13px;color:var(--t1)}.strct-submenu__trigger:hover{background:var(--bg-3)}.strct-submenu__trigger:focus-visible{outline:none;background:var(--bg-3)}.strct-submenu__icon{color:var(--t2);flex-shrink:0}.strct-submenu__icon-spacer{width:14px;flex-shrink:0}.strct-submenu__label{flex:1;display:inline-flex;align-items:center;gap:8px}.strct-submenu__arrow{color:var(--t3)}.strct-submenu__panel{position:absolute;top:-5px;left:100%;z-index:1;min-width:170px;margin-left:2px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-submenu-in .1s ease}.strct-submenu__panel--flip{left:auto;right:100%;margin-left:0;margin-right:2px}@keyframes strct-submenu-in{0%{opacity:0;transform:translate(-4px)}}\n"] }]
|
|
1691
1763
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
|
|
1692
1764
|
|
|
1693
1765
|
/**
|
|
@@ -2329,6 +2401,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
2329
2401
|
`, host: { class: 'strct-pg', role: 'navigation', 'aria-label': 'Pagination' }, styles: [".strct-pg{display:inline-flex;align-items:center;gap:4px}.strct-pg__btn{display:inline-flex;align-items:center;justify-content:center;min-width:30px;height:30px;padding:0 7px;border-radius:6px;font-family:var(--font);font-size:13px;cursor:pointer;color:var(--t1);background:transparent;border:1px solid transparent;transition:background .14s ease,border-color .14s ease,color .14s ease}.strct-pg__btn:hover{background:var(--bg-3)}.strct-pg__btn--active{color:var(--acc);border-color:var(--acc30);background:var(--acc-m)}.strct-pg__btn:disabled{color:var(--t4);cursor:not-allowed;background:transparent}.strct-pg__nav{color:var(--t2)}.strct-pg__dots{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:30px;color:var(--t3)}\n"] }]
|
|
2330
2402
|
}], propDecorators: { total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }] } });
|
|
2331
2403
|
|
|
2404
|
+
let fieldCounter = 0;
|
|
2405
|
+
/**
|
|
2406
|
+
* Form-field wrapper: a label (with optional required marker), the projected
|
|
2407
|
+
* control, and a hint or error message. It auto-links the control via
|
|
2408
|
+
* `aria-describedby` and sets `aria-invalid` when an error is present.
|
|
2409
|
+
*
|
|
2410
|
+
* <strct-field label="Email" required hint="We never share it." [error]="emailError()">
|
|
2411
|
+
* <input strctInput type="email" [(ngModel)]="email" />
|
|
2412
|
+
* </strct-field>
|
|
2413
|
+
*/
|
|
2414
|
+
class StrctField {
|
|
2415
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
2416
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2417
|
+
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
2418
|
+
/** Error message (string or first-of array); falsy clears the error state. */
|
|
2419
|
+
error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
2420
|
+
host = inject(ElementRef);
|
|
2421
|
+
n = ++fieldCounter;
|
|
2422
|
+
hintId = `strct-field-hint-${this.n}`;
|
|
2423
|
+
errorId = `strct-field-err-${this.n}`;
|
|
2424
|
+
controlId = signal('', ...(ngDevMode ? [{ debugName: "controlId" }] : /* istanbul ignore next */ []));
|
|
2425
|
+
errorText = computed(() => {
|
|
2426
|
+
const e = this.error();
|
|
2427
|
+
return (Array.isArray(e) ? e[0] : e) ?? '';
|
|
2428
|
+
}, ...(ngDevMode ? [{ debugName: "errorText" }] : /* istanbul ignore next */ []));
|
|
2429
|
+
constructor() {
|
|
2430
|
+
afterNextRender(() => this.link());
|
|
2431
|
+
// Keep aria in sync as the error / hint change.
|
|
2432
|
+
effect(() => {
|
|
2433
|
+
this.errorText();
|
|
2434
|
+
this.hint();
|
|
2435
|
+
this.applyAria();
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
control() {
|
|
2439
|
+
return this.host.nativeElement.querySelector('input, select, textarea, [strctInput], [strctField]');
|
|
2440
|
+
}
|
|
2441
|
+
link() {
|
|
2442
|
+
const el = this.control();
|
|
2443
|
+
if (!el)
|
|
2444
|
+
return;
|
|
2445
|
+
if (!el.id)
|
|
2446
|
+
el.id = `strct-field-ctrl-${this.n}`;
|
|
2447
|
+
this.controlId.set(el.id);
|
|
2448
|
+
this.applyAria();
|
|
2449
|
+
}
|
|
2450
|
+
applyAria() {
|
|
2451
|
+
const el = this.control();
|
|
2452
|
+
if (!el)
|
|
2453
|
+
return;
|
|
2454
|
+
const describedBy = this.errorText() ? this.errorId : this.hint() ? this.hintId : '';
|
|
2455
|
+
if (describedBy)
|
|
2456
|
+
el.setAttribute('aria-describedby', describedBy);
|
|
2457
|
+
else
|
|
2458
|
+
el.removeAttribute('aria-describedby');
|
|
2459
|
+
if (this.errorText())
|
|
2460
|
+
el.setAttribute('aria-invalid', 'true');
|
|
2461
|
+
else
|
|
2462
|
+
el.removeAttribute('aria-invalid');
|
|
2463
|
+
}
|
|
2464
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2465
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctField, isStandalone: true, selector: "strct-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.strct-field--invalid": "!!errorText()" }, classAttribute: "strct-field" }, ngImport: i0, template: `
|
|
2466
|
+
@if (label()) {
|
|
2467
|
+
<label class="strct-field__label" [attr.for]="controlId() || null">
|
|
2468
|
+
{{ label() }}@if (required()) {<span class="strct-field__req" aria-hidden="true">*</span>}
|
|
2469
|
+
</label>
|
|
2470
|
+
}
|
|
2471
|
+
<div class="strct-field__control"><ng-content /></div>
|
|
2472
|
+
@if (errorText()) {
|
|
2473
|
+
<div class="strct-field__msg strct-field__msg--error" [id]="errorId" role="alert">
|
|
2474
|
+
{{ errorText() }}
|
|
2475
|
+
</div>
|
|
2476
|
+
} @else if (hint()) {
|
|
2477
|
+
<div class="strct-field__msg strct-field__msg--hint" [id]="hintId">{{ hint() }}</div>
|
|
2478
|
+
}
|
|
2479
|
+
`, isInline: true, styles: [".strct-field{display:flex;flex-direction:column;gap:6px}.strct-field__label{font-size:12px;font-weight:600;color:var(--t2)}.strct-field__req{color:var(--crt);margin-left:2px}.strct-field__control{display:flex;flex-direction:column}.strct-field__msg{font-size:12px;line-height:1.4}.strct-field__msg--hint{color:var(--t3)}.strct-field__msg--error{color:var(--crt)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2480
|
+
}
|
|
2481
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctField, decorators: [{
|
|
2482
|
+
type: Component,
|
|
2483
|
+
args: [{ selector: 'strct-field', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
|
|
2484
|
+
@if (label()) {
|
|
2485
|
+
<label class="strct-field__label" [attr.for]="controlId() || null">
|
|
2486
|
+
{{ label() }}@if (required()) {<span class="strct-field__req" aria-hidden="true">*</span>}
|
|
2487
|
+
</label>
|
|
2488
|
+
}
|
|
2489
|
+
<div class="strct-field__control"><ng-content /></div>
|
|
2490
|
+
@if (errorText()) {
|
|
2491
|
+
<div class="strct-field__msg strct-field__msg--error" [id]="errorId" role="alert">
|
|
2492
|
+
{{ errorText() }}
|
|
2493
|
+
</div>
|
|
2494
|
+
} @else if (hint()) {
|
|
2495
|
+
<div class="strct-field__msg strct-field__msg--hint" [id]="hintId">{{ hint() }}</div>
|
|
2496
|
+
}
|
|
2497
|
+
`, host: {
|
|
2498
|
+
class: 'strct-field',
|
|
2499
|
+
'[class.strct-field--invalid]': '!!errorText()',
|
|
2500
|
+
}, styles: [".strct-field{display:flex;flex-direction:column;gap:6px}.strct-field__label{font-size:12px;font-weight:600;color:var(--t2)}.strct-field__req{color:var(--crt);margin-left:2px}.strct-field__control{display:flex;flex-direction:column}.strct-field__msg{font-size:12px;line-height:1.4}.strct-field__msg--hint{color:var(--t3)}.strct-field__msg--error{color:var(--crt)}\n"] }]
|
|
2501
|
+
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }] } });
|
|
2502
|
+
|
|
2332
2503
|
/**
|
|
2333
2504
|
* Applies the shared `.strct-control` look to a native input / textarea / select.
|
|
2334
2505
|
* <input strctInput placeholder="Name" />
|
|
@@ -5930,5 +6101,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
5930
6101
|
* Generated bundle index. Do not edit.
|
|
5931
6102
|
*/
|
|
5932
6103
|
|
|
5933
|
-
export { STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_MASKS, STRCT_PALETTES, STRCT_RAW_ICONS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCascadeHost, StrctCascadeNode, StrctCascadeSelect, StrctCellDef, StrctChart, StrctCheckbox, StrctChips, StrctColorPicker, StrctCombobox, StrctContextMenu, StrctContextMenuTrigger, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDivider, StrctDonut, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctFile, StrctFooter, StrctGauge, StrctHeader, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKnob, StrctLogin, StrctMenuPanel, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRange, StrctRating, StrctRowDetailDef, StrctShell, StrctSignpost, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctStack, StrctStackItem, StrctStep, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctThemeService, StrctThemeSwitcher, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctTooltip, StrctTree, StrctTreeNode, StrctVerticalNav, StrctWizard, registerStrctIcon };
|
|
6104
|
+
export { STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_MASKS, STRCT_PALETTES, STRCT_RAW_ICONS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCascadeHost, StrctCascadeNode, StrctCascadeSelect, StrctCellDef, StrctChart, StrctCheckbox, StrctChips, StrctColorPicker, StrctCombobox, StrctContextMenu, StrctContextMenuTrigger, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDivider, StrctDonut, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctField, StrctFile, StrctFooter, StrctGauge, StrctHeader, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKnob, StrctLogin, StrctMenuPanel, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRange, StrctRating, StrctRowDetailDef, StrctShell, StrctSignpost, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctStack, StrctStackItem, StrctStep, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctThemeService, StrctThemeSwitcher, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctTooltip, StrctTree, StrctTreeNode, StrctVerticalNav, StrctWizard, registerStrctIcon };
|
|
5934
6105
|
//# sourceMappingURL=akcelik-strct.mjs.map
|