@akcelik/strct 1.3.0 → 1.6.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/fesm2022/akcelik-strct.mjs +707 -36
- package/fesm2022/akcelik-strct.mjs.map +1 -1
- package/package.json +1 -1
- package/types/akcelik-strct.d.ts +439 -14
|
@@ -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, model, output, booleanAttribute, HostListener, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, DestroyRef, contentChild, viewChild, viewChildren, untracked, TemplateRef, Renderer2 } from '@angular/core';
|
|
2
|
+
import { inject, DOCUMENT, signal, computed, Injectable, input, isDevMode, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, model, output, booleanAttribute, HostListener, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, DestroyRef, contentChild, viewChild, viewChildren, untracked, TemplateRef, Renderer2 } from '@angular/core';
|
|
3
3
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
4
|
import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1 } from '@angular/common';
|
|
5
5
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
@@ -135,7 +135,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
135
135
|
* storage, VMs …). Object state — running / stopped / maintenance — is layered
|
|
136
136
|
* on with the `badge` input rather than a separate icon per state.
|
|
137
137
|
*/
|
|
138
|
-
|
|
138
|
+
// Built-in stroke glyphs. Keys stay inferred so \`StrctIconName\` below is a
|
|
139
|
+
// compile-time union of every name.
|
|
140
|
+
const STRCT_ICON_DEFS = {
|
|
139
141
|
// ── General UI ───────────────────────────────────────────────
|
|
140
142
|
hexagon: '<path d="M8 1.6l5.5 3.2v6.4L8 14.4 2.5 11.2V4.8L8 1.6z"/>',
|
|
141
143
|
chevronRight: '<path d="M6 3.5L10.5 8 6 12.5"/>',
|
|
@@ -254,6 +256,7 @@ const STRCT_ICONS = {
|
|
|
254
256
|
megaphone: '<path d="M3 7.3v1.6l7 3V4.3zM10 5.7 12.5 4.2v7.6L10 10.3M4 9.6l.9 3.1h1.6l-.7-2.9"/>',
|
|
255
257
|
flag: '<path d="M4 13.5V3M4 3.6h7.4L9.8 6.2l1.6 2.6H4"/>',
|
|
256
258
|
shieldAlert: '<path d="M8 2.3l5.2 1.9v3.3c0 3.1-2.3 5.1-5.2 6.4C5.1 12.6 2.8 10.6 2.8 7.5V4.2z"/><path d="M8 5.5v3.1M8 10.6v.2"/>',
|
|
259
|
+
shieldCheck: '<path d="M8 2.3l5.2 1.9v3.3c0 3.1-2.3 5.1-5.2 6.4C5.1 12.6 2.8 10.6 2.8 7.5V4.2z"/><path d="M5.9 7.9l1.5 1.5 2.8-2.9"/>',
|
|
257
260
|
// ── State / action (also usable as badges via the icon `badge` input) ─
|
|
258
261
|
running: '<path d="M5.5 4l6 4-6 4z"/>',
|
|
259
262
|
stopped: '<rect x="4.5" y="4.5" width="7" height="7" rx="1"/>',
|
|
@@ -365,15 +368,26 @@ const STRCT_ICONS = {
|
|
|
365
368
|
// Printer — paper feed, body, output sheet and an LED.
|
|
366
369
|
printer: '<path d="M5 6.4V3.2h6v3.2"/><rect x="2.8" y="6.4" width="10.4" height="4.8" rx="1"/><path d="M5 9.2h6v3.6H5z"/><circle cx="11" cy="8.4" r=".5" fill="currentColor" stroke="none"/>',
|
|
367
370
|
};
|
|
368
|
-
// Composite "off" variants — re-use base glyph + diagonal slash.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
371
|
+
// Composite "off" variants — re-use base glyph + diagonal slash — and aliases.
|
|
372
|
+
const STRCT_ICON_DERIVED_DEFS = {
|
|
373
|
+
eyeOff: `${STRCT_ICON_DEFS.eye}<path d="M2.5 2.5l11 11"/>`,
|
|
374
|
+
bellOff: `${STRCT_ICON_DEFS.bell}<path d="M2.6 2.6l10.8 10.8"/>`,
|
|
375
|
+
wifiOff: `${STRCT_ICON_DEFS.wifi}<path d="M2.5 2.5l11 11"/>`,
|
|
376
|
+
cloudOff: `${STRCT_ICON_DEFS.cloud}<path d="M2.5 2.5l11 11"/>`,
|
|
377
|
+
linkOff: `${STRCT_ICON_DEFS.link}<path d="M2.5 2.5l11 11"/>`,
|
|
378
|
+
unlink: `${STRCT_ICON_DEFS.link}<path d="M2.5 2.5l11 11"/>`,
|
|
379
|
+
webhook: STRCT_ICON_DEFS.bolt,
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Icon name → glyph markup. Open (`Record<string, string>`) on purpose so
|
|
383
|
+
* `registerStrctIcon` can add app-specific names at runtime.
|
|
384
|
+
*/
|
|
385
|
+
const STRCT_ICONS = {
|
|
386
|
+
...STRCT_ICON_DEFS,
|
|
387
|
+
...STRCT_ICON_DERIVED_DEFS,
|
|
388
|
+
};
|
|
389
|
+
/** All built-in icon names — greppable, iterable (galleries, docs, tests). */
|
|
390
|
+
const STRCT_ICON_NAMES = Object.keys(STRCT_ICONS).sort();
|
|
377
391
|
/** Icon names grouped for galleries / documentation. */
|
|
378
392
|
const STRCT_ICON_GROUPS = [
|
|
379
393
|
{
|
|
@@ -514,6 +528,7 @@ const STRCT_ICON_GROUPS = [
|
|
|
514
528
|
'container',
|
|
515
529
|
'firewall',
|
|
516
530
|
'shield',
|
|
531
|
+
'shieldCheck',
|
|
517
532
|
'certificate',
|
|
518
533
|
'key',
|
|
519
534
|
'metrics',
|
|
@@ -585,9 +600,15 @@ function registerStrctIcon(name, content, options = {}) {
|
|
|
585
600
|
* glyph with a green status dot (a "running host"). Unknown names render
|
|
586
601
|
* nothing rather than throwing.
|
|
587
602
|
*/
|
|
603
|
+
// One warning per unknown name per app run — enough to surface the bug
|
|
604
|
+
// without flooding the console from a list render.
|
|
605
|
+
const warnedUnknownIcons = new Set();
|
|
588
606
|
class StrctIcon {
|
|
589
607
|
sanitizer = inject(DomSanitizer);
|
|
590
|
-
/**
|
|
608
|
+
/**
|
|
609
|
+
* Icon name. Typed to autocomplete the built-in set while still accepting
|
|
610
|
+
* any string, since `registerStrctIcon` can add names at runtime.
|
|
611
|
+
*/
|
|
591
612
|
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
592
613
|
/** Icon size in pixels. */
|
|
593
614
|
size = input(16, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
@@ -599,7 +620,17 @@ class StrctIcon {
|
|
|
599
620
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
600
621
|
/** True when the named icon is a full-SVG (raw) icon registered by the app. */
|
|
601
622
|
isRaw = computed(() => Object.prototype.hasOwnProperty.call(STRCT_RAW_ICONS, this.name()), ...(ngDevMode ? [{ debugName: "isRaw" }] : /* istanbul ignore next */ []));
|
|
602
|
-
svg = computed(() =>
|
|
623
|
+
svg = computed(() => {
|
|
624
|
+
const name = this.name();
|
|
625
|
+
const glyph = STRCT_ICONS[name];
|
|
626
|
+
if (glyph === undefined && !this.isRaw() && isDevMode() && !warnedUnknownIcons.has(name)) {
|
|
627
|
+
warnedUnknownIcons.add(name);
|
|
628
|
+
console.warn(`[strct-icon] Unknown icon name "${name}" — it will render empty. ` +
|
|
629
|
+
`Built-in names are exported as STRCT_ICON_NAMES (type StrctIconName); ` +
|
|
630
|
+
`custom icons must be added with registerStrctIcon().`);
|
|
631
|
+
}
|
|
632
|
+
return this.sanitizer.bypassSecurityTrustHtml(glyph ?? '');
|
|
633
|
+
}, ...(ngDevMode ? [{ debugName: "svg" }] : /* istanbul ignore next */ []));
|
|
603
634
|
rawSvg = computed(() => this.sanitizer.bypassSecurityTrustHtml(STRCT_RAW_ICONS[this.name()] ?? ''), ...(ngDevMode ? [{ debugName: "rawSvg" }] : /* istanbul ignore next */ []));
|
|
604
635
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
605
636
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctIcon, isStandalone: true, selector: "strct-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-icon" }, ngImport: i0, template: `@if (isRaw()) {
|
|
@@ -1326,6 +1357,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
1326
1357
|
`, host: { class: 'strct-login' }, styles: [".strct-login{display:flex;align-items:center;justify-content:center;width:100%;min-height:100%;padding:32px}.strct-login__inner{width:100%}.strct-login__card{display:grid;grid-template-columns:1.05fr 1fr;width:100%;overflow:hidden}.strct-login__main{padding:40px 38px;display:flex;flex-direction:column;justify-content:center}.strct-login__aside{position:relative;overflow:hidden;min-height:420px;color:var(--t1);border-inline-end:1px solid var(--b2)}.strct-login__aside:before,.strct-login__aside:after{display:none}.strct-login__aside-inner{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;justify-content:space-between;padding:34px 36px}@media(max-width:720px){.strct-login__card{grid-template-columns:1fr}.strct-login__aside{min-height:220px}.strct-login__main{padding:30px 26px}}\n"] }]
|
|
1327
1358
|
}], propDecorators: { maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], split: [{ type: i0.Input, args: [{ isSignal: true, alias: "split", required: false }] }] } });
|
|
1328
1359
|
|
|
1360
|
+
/** Marks the breadcrumb row projected above the title. */
|
|
1361
|
+
class StrctPageHeaderCrumbs {
|
|
1362
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeaderCrumbs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1363
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.16", type: StrctPageHeaderCrumbs, isStandalone: true, selector: "[strctPageHeaderCrumbs]", ngImport: i0 });
|
|
1364
|
+
}
|
|
1365
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeaderCrumbs, decorators: [{
|
|
1366
|
+
type: Directive,
|
|
1367
|
+
args: [{ selector: '[strctPageHeaderCrumbs]' }]
|
|
1368
|
+
}] });
|
|
1369
|
+
/** Marks the action buttons aligned to the end of the title row. */
|
|
1370
|
+
class StrctPageHeaderActions {
|
|
1371
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeaderActions, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1372
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.16", type: StrctPageHeaderActions, isStandalone: true, selector: "[strctPageHeaderActions]", ngImport: i0 });
|
|
1373
|
+
}
|
|
1374
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeaderActions, decorators: [{
|
|
1375
|
+
type: Directive,
|
|
1376
|
+
args: [{ selector: '[strctPageHeaderActions]' }]
|
|
1377
|
+
}] });
|
|
1378
|
+
/**
|
|
1379
|
+
* Page header — the top of every console object page: an optional breadcrumb
|
|
1380
|
+
* row, a title + subtitle, and actions aligned to the end. Default content
|
|
1381
|
+
* projects below the title row (status badges, meta strips).
|
|
1382
|
+
*
|
|
1383
|
+
* <strct-page-header title="hv-02" subtitle="Hypervisor · cluster-01">
|
|
1384
|
+
* <strct-breadcrumb strctPageHeaderCrumbs>…</strct-breadcrumb>
|
|
1385
|
+
* <button strct-button strctPageHeaderActions variant="primary">Migrate</button>
|
|
1386
|
+
* </strct-page-header>
|
|
1387
|
+
*/
|
|
1388
|
+
class StrctPageHeader {
|
|
1389
|
+
/** Page title (an h1). */
|
|
1390
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1391
|
+
/** One-line description under the title. */
|
|
1392
|
+
subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
1393
|
+
/** Draw a hairline divider under the header. */
|
|
1394
|
+
divider = input(false, { ...(ngDevMode ? { debugName: "divider" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1395
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1396
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctPageHeader, isStandalone: true, selector: "strct-page-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.strct-ph--divider": "divider()" }, classAttribute: "strct-ph" }, ngImport: i0, template: `
|
|
1397
|
+
<div class="strct-ph__crumbs"><ng-content select="[strctPageHeaderCrumbs]" /></div>
|
|
1398
|
+
<div class="strct-ph__row">
|
|
1399
|
+
<div class="strct-ph__text">
|
|
1400
|
+
<h1 class="strct-ph__title">{{ title() }}</h1>
|
|
1401
|
+
@if (subtitle()) {
|
|
1402
|
+
<p class="strct-ph__subtitle">{{ subtitle() }}</p>
|
|
1403
|
+
}
|
|
1404
|
+
</div>
|
|
1405
|
+
<div class="strct-ph__actions"><ng-content select="[strctPageHeaderActions]" /></div>
|
|
1406
|
+
</div>
|
|
1407
|
+
<ng-content />
|
|
1408
|
+
`, isInline: true, styles: [".strct-ph{display:block}.strct-ph--divider{padding-bottom:16px;border-bottom:1px solid var(--b1)}.strct-ph__crumbs:empty{display:none}.strct-ph__crumbs{margin-bottom:6px}.strct-ph__row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.strct-ph__text{flex:1;min-width:0}.strct-ph__title{margin:0;font-size:var(--text-2xl);font-weight:700;letter-spacing:-.01em;color:var(--t1);text-wrap:balance}.strct-ph__subtitle{margin:6px 0 0;font-size:13px;line-height:1.55;color:var(--t2);max-width:72ch}.strct-ph__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.strct-ph__actions:empty{display:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1409
|
+
}
|
|
1410
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctPageHeader, decorators: [{
|
|
1411
|
+
type: Component,
|
|
1412
|
+
args: [{ selector: 'strct-page-header', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
|
|
1413
|
+
<div class="strct-ph__crumbs"><ng-content select="[strctPageHeaderCrumbs]" /></div>
|
|
1414
|
+
<div class="strct-ph__row">
|
|
1415
|
+
<div class="strct-ph__text">
|
|
1416
|
+
<h1 class="strct-ph__title">{{ title() }}</h1>
|
|
1417
|
+
@if (subtitle()) {
|
|
1418
|
+
<p class="strct-ph__subtitle">{{ subtitle() }}</p>
|
|
1419
|
+
}
|
|
1420
|
+
</div>
|
|
1421
|
+
<div class="strct-ph__actions"><ng-content select="[strctPageHeaderActions]" /></div>
|
|
1422
|
+
</div>
|
|
1423
|
+
<ng-content />
|
|
1424
|
+
`, host: {
|
|
1425
|
+
class: 'strct-ph',
|
|
1426
|
+
'[class.strct-ph--divider]': 'divider()',
|
|
1427
|
+
}, styles: [".strct-ph{display:block}.strct-ph--divider{padding-bottom:16px;border-bottom:1px solid var(--b1)}.strct-ph__crumbs:empty{display:none}.strct-ph__crumbs{margin-bottom:6px}.strct-ph__row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.strct-ph__text{flex:1;min-width:0}.strct-ph__title{margin:0;font-size:var(--text-2xl);font-weight:700;letter-spacing:-.01em;color:var(--t1);text-wrap:balance}.strct-ph__subtitle{margin:6px 0 0;font-size:13px;line-height:1.55;color:var(--t2);max-width:72ch}.strct-ph__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.strct-ph__actions:empty{display:none}\n"] }]
|
|
1428
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }] } });
|
|
1429
|
+
|
|
1329
1430
|
/**
|
|
1330
1431
|
* Styles a native `<button>` / `<a>` so it stays fully accessible and form-aware.
|
|
1331
1432
|
*
|
|
@@ -3256,11 +3357,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
3256
3357
|
* <strct-dropdown-item>Rename</strct-dropdown-item>
|
|
3257
3358
|
* <strct-dropdown-item critical>Delete</strct-dropdown-item>
|
|
3258
3359
|
* </strct-dropdown>
|
|
3360
|
+
*
|
|
3361
|
+
* With `popover`, the panel holds *form controls* instead of menu items: an
|
|
3362
|
+
* inner click no longer closes it (only outside click / Escape do), and the
|
|
3363
|
+
* semantics switch from `role="menu"` to a labeled `role="dialog"` — a panel
|
|
3364
|
+
* of selects is not a menu to a screen reader either:
|
|
3365
|
+
*
|
|
3366
|
+
* <strct-dropdown popover popoverLabel="Filters">
|
|
3367
|
+
* <button strct-button strctDropdownTrigger>Filters</button>
|
|
3368
|
+
* <strct-field label="Severity">…</strct-field>
|
|
3369
|
+
* </strct-dropdown>
|
|
3259
3370
|
*/
|
|
3260
3371
|
class StrctDropdown {
|
|
3261
3372
|
host = inject((ElementRef));
|
|
3262
3373
|
/** Horizontal alignment of the menu. */
|
|
3263
3374
|
align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
3375
|
+
/**
|
|
3376
|
+
* Popover mode for panels holding form controls (filter/settings panels):
|
|
3377
|
+
* inner clicks never close the panel — only outside click / Escape do —
|
|
3378
|
+
* and it renders as a labeled `role="dialog"` instead of a menu.
|
|
3379
|
+
*/
|
|
3380
|
+
popover = input(false, { ...(ngDevMode ? { debugName: "popover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3381
|
+
/** Accessible name of the popover dialog (localizable). */
|
|
3382
|
+
popoverLabel = input('Filters', ...(ngDevMode ? [{ debugName: "popoverLabel" }] : /* istanbul ignore next */ []));
|
|
3264
3383
|
open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
3265
3384
|
toggle() {
|
|
3266
3385
|
this.open.update((v) => !v);
|
|
@@ -3268,6 +3387,11 @@ class StrctDropdown {
|
|
|
3268
3387
|
close() {
|
|
3269
3388
|
this.open.set(false);
|
|
3270
3389
|
}
|
|
3390
|
+
/** Menu items close on activation; popover form controls never do. */
|
|
3391
|
+
onInnerActivate() {
|
|
3392
|
+
if (!this.popover())
|
|
3393
|
+
this.close();
|
|
3394
|
+
}
|
|
3271
3395
|
onDocClick(event) {
|
|
3272
3396
|
if (this.open() && !this.host.nativeElement.contains(event.target)) {
|
|
3273
3397
|
this.close();
|
|
@@ -3277,33 +3401,41 @@ class StrctDropdown {
|
|
|
3277
3401
|
this.close();
|
|
3278
3402
|
}
|
|
3279
3403
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3280
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDropdown, isStandalone: true, selector: "strct-dropdown", inputs: { align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocClick($event)", "document:keydown.escape": "onEscape()" }, classAttribute: "strct-dd" }, ngImport: i0, template: `
|
|
3404
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDropdown, isStandalone: true, selector: "strct-dropdown", inputs: { align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, popover: { classPropertyName: "popover", publicName: "popover", isSignal: true, isRequired: false, transformFunction: null }, popoverLabel: { classPropertyName: "popoverLabel", publicName: "popoverLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocClick($event)", "document:keydown.escape": "onEscape()" }, classAttribute: "strct-dd" }, ngImport: i0, template: `
|
|
3281
3405
|
<div
|
|
3282
3406
|
#trigger
|
|
3283
3407
|
class="strct-dd__trigger"
|
|
3284
3408
|
role="button"
|
|
3285
3409
|
tabindex="0"
|
|
3410
|
+
[attr.aria-haspopup]="popover() ? 'dialog' : 'menu'"
|
|
3411
|
+
[attr.aria-expanded]="open()"
|
|
3286
3412
|
(click)="toggle()"
|
|
3287
3413
|
(keydown.enter)="toggle()"
|
|
3288
3414
|
(keydown.space)="toggle()"
|
|
3289
3415
|
>
|
|
3290
3416
|
<ng-content select="[strctDropdownTrigger]" />
|
|
3291
3417
|
</div>
|
|
3418
|
+
<!--
|
|
3419
|
+
One panel for both modes (a default ng-content per @if branch would strand
|
|
3420
|
+
the projected content in the inactive branch) — mode picks the semantics.
|
|
3421
|
+
-->
|
|
3292
3422
|
@if (open()) {
|
|
3293
3423
|
<div
|
|
3294
3424
|
class="strct-dd__menu"
|
|
3425
|
+
[class.strct-dd__menu--popover]="popover()"
|
|
3295
3426
|
[strctOverlay]="trigger"
|
|
3296
3427
|
[strctOverlayPlacement]="align() === 'end' ? 'bottom-end' : 'bottom-start'"
|
|
3297
|
-
role="menu"
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
(
|
|
3301
|
-
(keydown.
|
|
3428
|
+
[attr.role]="popover() ? 'dialog' : 'menu'"
|
|
3429
|
+
[attr.aria-label]="popover() ? popoverLabel() : null"
|
|
3430
|
+
[attr.tabindex]="popover() ? -1 : 0"
|
|
3431
|
+
(click)="onInnerActivate()"
|
|
3432
|
+
(keydown.enter)="onInnerActivate()"
|
|
3433
|
+
(keydown.space)="onInnerActivate()"
|
|
3302
3434
|
>
|
|
3303
3435
|
<ng-content />
|
|
3304
3436
|
</div>
|
|
3305
3437
|
}
|
|
3306
|
-
`, isInline: true, styles: [".strct-dd{position:relative;display:inline-block}.strct-dd__trigger{display:inline-flex}.strct-dd__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:200;min-width:170px;max-width:calc(100vw - 24px);padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-dd-in .1s ease}.strct-dd__menu--end{left:auto;right:0}@keyframes strct-dd-in{0%{opacity:0;transform:translateY(-4px)}}\n"], dependencies: [{ kind: "directive", type: StrctOverlay, selector: "[strctOverlay]", inputs: ["strctOverlay", "strctOverlayPlacement", "strctOverlayMatchWidth", "strctOverlayGap"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
3438
|
+
`, isInline: true, styles: [".strct-dd{position:relative;display:inline-block}.strct-dd__trigger{display:inline-flex}.strct-dd__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:200;min-width:170px;max-width:calc(100vw - 24px);padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-dd-in .1s ease}.strct-dd__menu--end{left:auto;right:0}.strct-dd__menu--popover{min-width:240px;padding:12px 14px}@keyframes strct-dd-in{0%{opacity:0;transform:translateY(-4px)}}\n"], dependencies: [{ kind: "directive", type: StrctOverlay, selector: "[strctOverlay]", inputs: ["strctOverlay", "strctOverlayPlacement", "strctOverlayMatchWidth", "strctOverlayGap"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
3307
3439
|
}
|
|
3308
3440
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDropdown, decorators: [{
|
|
3309
3441
|
type: Component,
|
|
@@ -3313,28 +3445,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
3313
3445
|
class="strct-dd__trigger"
|
|
3314
3446
|
role="button"
|
|
3315
3447
|
tabindex="0"
|
|
3448
|
+
[attr.aria-haspopup]="popover() ? 'dialog' : 'menu'"
|
|
3449
|
+
[attr.aria-expanded]="open()"
|
|
3316
3450
|
(click)="toggle()"
|
|
3317
3451
|
(keydown.enter)="toggle()"
|
|
3318
3452
|
(keydown.space)="toggle()"
|
|
3319
3453
|
>
|
|
3320
3454
|
<ng-content select="[strctDropdownTrigger]" />
|
|
3321
3455
|
</div>
|
|
3456
|
+
<!--
|
|
3457
|
+
One panel for both modes (a default ng-content per @if branch would strand
|
|
3458
|
+
the projected content in the inactive branch) — mode picks the semantics.
|
|
3459
|
+
-->
|
|
3322
3460
|
@if (open()) {
|
|
3323
3461
|
<div
|
|
3324
3462
|
class="strct-dd__menu"
|
|
3463
|
+
[class.strct-dd__menu--popover]="popover()"
|
|
3325
3464
|
[strctOverlay]="trigger"
|
|
3326
3465
|
[strctOverlayPlacement]="align() === 'end' ? 'bottom-end' : 'bottom-start'"
|
|
3327
|
-
role="menu"
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
(
|
|
3331
|
-
(keydown.
|
|
3466
|
+
[attr.role]="popover() ? 'dialog' : 'menu'"
|
|
3467
|
+
[attr.aria-label]="popover() ? popoverLabel() : null"
|
|
3468
|
+
[attr.tabindex]="popover() ? -1 : 0"
|
|
3469
|
+
(click)="onInnerActivate()"
|
|
3470
|
+
(keydown.enter)="onInnerActivate()"
|
|
3471
|
+
(keydown.space)="onInnerActivate()"
|
|
3332
3472
|
>
|
|
3333
3473
|
<ng-content />
|
|
3334
3474
|
</div>
|
|
3335
3475
|
}
|
|
3336
|
-
`, host: { class: 'strct-dd' }, styles: [".strct-dd{position:relative;display:inline-block}.strct-dd__trigger{display:inline-flex}.strct-dd__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:200;min-width:170px;max-width:calc(100vw - 24px);padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-dd-in .1s ease}.strct-dd__menu--end{left:auto;right:0}@keyframes strct-dd-in{0%{opacity:0;transform:translateY(-4px)}}\n"] }]
|
|
3337
|
-
}], propDecorators: { align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], onDocClick: [{
|
|
3476
|
+
`, host: { class: 'strct-dd' }, styles: [".strct-dd{position:relative;display:inline-block}.strct-dd__trigger{display:inline-flex}.strct-dd__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:200;min-width:170px;max-width:calc(100vw - 24px);padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-dd-in .1s ease}.strct-dd__menu--end{left:auto;right:0}.strct-dd__menu--popover{min-width:240px;padding:12px 14px}@keyframes strct-dd-in{0%{opacity:0;transform:translateY(-4px)}}\n"] }]
|
|
3477
|
+
}], propDecorators: { align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], popover: [{ type: i0.Input, args: [{ isSignal: true, alias: "popover", required: false }] }], popoverLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverLabel", required: false }] }], onDocClick: [{
|
|
3338
3478
|
type: HostListener,
|
|
3339
3479
|
args: ['document:click', ['$event']]
|
|
3340
3480
|
}], onEscape: [{
|
|
@@ -4139,9 +4279,24 @@ class StrctCommandPalette {
|
|
|
4139
4279
|
emptyText = input('No matches for', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
|
|
4140
4280
|
/** Maximum rendered results. */
|
|
4141
4281
|
maxResults = input(50, ...(ngDevMode ? [{ debugName: "maxResults" }] : /* istanbul ignore next */ []));
|
|
4282
|
+
/**
|
|
4283
|
+
* Internal ranked filtering. Set `false` for a **server-backed** palette:
|
|
4284
|
+
* items render in the order given (you already filtered them) and the query
|
|
4285
|
+
* reaches you through the two-way `query` model. `maxResults` still caps
|
|
4286
|
+
* rendering.
|
|
4287
|
+
*/
|
|
4288
|
+
filter = input(true, { ...(ngDevMode ? { debugName: "filter" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4289
|
+
/** Show a "searching" row while server results are in flight. */
|
|
4290
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4291
|
+
/** Localizable text of the loading row. */
|
|
4292
|
+
loadingText = input('Searching…', ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
|
|
4142
4293
|
/** Emitted when a command is chosen (Enter or click); the palette closes. */
|
|
4143
4294
|
picked = output();
|
|
4144
|
-
|
|
4295
|
+
/**
|
|
4296
|
+
* The typed query (two-way). The palette still owns typing and keyboard
|
|
4297
|
+
* behaviour; consumers observe it to serve results asynchronously.
|
|
4298
|
+
*/
|
|
4299
|
+
query = model('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
4145
4300
|
activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
4146
4301
|
listId = `strct-cmdp-list-${++paletteCounter}`;
|
|
4147
4302
|
previousActive = null;
|
|
@@ -4150,8 +4305,11 @@ class StrctCommandPalette {
|
|
|
4150
4305
|
}
|
|
4151
4306
|
/** Rank: label prefix > word-start > substring; group/keyword matches last. */
|
|
4152
4307
|
filtered = computed(() => {
|
|
4153
|
-
const q = this.query().trim().toLowerCase();
|
|
4154
4308
|
const items = this.items();
|
|
4309
|
+
// Server-backed mode: the caller already filtered — render as given.
|
|
4310
|
+
if (!this.filter())
|
|
4311
|
+
return items.slice(0, this.maxResults());
|
|
4312
|
+
const q = this.query().trim().toLowerCase();
|
|
4155
4313
|
if (!q)
|
|
4156
4314
|
return items.slice(0, this.maxResults());
|
|
4157
4315
|
const scored = [];
|
|
@@ -4176,6 +4334,12 @@ class StrctCommandPalette {
|
|
|
4176
4334
|
.map((s) => s.item);
|
|
4177
4335
|
}, ...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
|
|
4178
4336
|
constructor() {
|
|
4337
|
+
// Async results can shrink under the cursor — keep the active option in range.
|
|
4338
|
+
effect(() => {
|
|
4339
|
+
const len = this.filtered().length;
|
|
4340
|
+
if (this.activeIndex() >= len)
|
|
4341
|
+
this.activeIndex.set(Math.max(0, len - 1));
|
|
4342
|
+
});
|
|
4179
4343
|
// Focus the input on open; restore focus on close.
|
|
4180
4344
|
effect(() => {
|
|
4181
4345
|
if (this.open()) {
|
|
@@ -4247,7 +4411,7 @@ class StrctCommandPalette {
|
|
|
4247
4411
|
this.open.set(false);
|
|
4248
4412
|
}
|
|
4249
4413
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCommandPalette, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4250
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctCommandPalette, isStandalone: true, selector: "strct-command-palette", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, hotkey: { classPropertyName: "hotkey", publicName: "hotkey", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", picked: "picked" }, host: { listeners: { "document:keydown": "onGlobalKey($event)" } }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4414
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctCommandPalette, isStandalone: true, selector: "strct-command-palette", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, hotkey: { classPropertyName: "hotkey", publicName: "hotkey", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", picked: "picked", query: "queryChange" }, host: { listeners: { "document:keydown": "onGlobalKey($event)" } }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4251
4415
|
@if (open()) {
|
|
4252
4416
|
<!-- Backdrop: pointer-only dismiss (Escape covers keyboard). -->
|
|
4253
4417
|
<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
|
|
@@ -4278,6 +4442,11 @@ class StrctCommandPalette {
|
|
|
4278
4442
|
<strct-kbd>Esc</strct-kbd>
|
|
4279
4443
|
</div>
|
|
4280
4444
|
<div class="strct-cmdp__list" role="listbox" [id]="listId">
|
|
4445
|
+
@if (loading()) {
|
|
4446
|
+
<div class="strct-cmdp__loading" aria-live="polite">
|
|
4447
|
+
<span class="strct-cmdp__spin" aria-hidden="true"></span>{{ loadingText() }}
|
|
4448
|
+
</div>
|
|
4449
|
+
}
|
|
4281
4450
|
@for (item of filtered(); track item.id; let i = $index) {
|
|
4282
4451
|
<button
|
|
4283
4452
|
type="button"
|
|
@@ -4307,13 +4476,15 @@ class StrctCommandPalette {
|
|
|
4307
4476
|
}
|
|
4308
4477
|
</button>
|
|
4309
4478
|
} @empty {
|
|
4310
|
-
|
|
4479
|
+
@if (!loading()) {
|
|
4480
|
+
<div class="strct-cmdp__empty">{{ emptyText() }} “{{ query() }}”</div>
|
|
4481
|
+
}
|
|
4311
4482
|
}
|
|
4312
4483
|
</div>
|
|
4313
4484
|
</div>
|
|
4314
4485
|
</div>
|
|
4315
4486
|
}
|
|
4316
|
-
`, isInline: true, styles: [".strct-cmdp__backdrop{position:fixed;inset:0;z-index:1200;display:flex;justify-content:center;align-items:flex-start;padding:14vh 16px 16px;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:strct-cmdp-fade .12s ease}.strct-cmdp{width:100%;max-width:560px;max-height:60vh;display:flex;flex-direction:column;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-xl);box-shadow:var(--shadow-floating);overflow:hidden;animation:strct-cmdp-rise .14s ease}.strct-cmdp__search{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--b1);color:var(--t3)}.strct-cmdp__input{flex:1;min-width:0;border:0;background:transparent;color:var(--t1);font-size:15px;font-family:var(--font);outline:none}.strct-cmdp__list{overflow-y:auto;padding:6px}.strct-cmdp__item{display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;border:0;border-radius:var(--radius-md);background:transparent;cursor:pointer;text-align:start;font-family:var(--font)}.strct-cmdp__item.is-active{background:var(--acc-m)}.strct-cmdp__icon{color:var(--t2);flex-shrink:0}.strct-cmdp__item.is-active .strct-cmdp__icon{color:var(--acc)}.strct-cmdp__label{flex:1;min-width:0;font-size:var(--text-lg);color:var(--t1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-cmdp__group{font-size:var(--text-sm);color:var(--t3);flex-shrink:0}.strct-cmdp__empty{padding:20px;text-align:center;font-size:var(--text-md);color:var(--t3)}@keyframes strct-cmdp-fade{0%{opacity:0}}@keyframes strct-cmdp-rise{0%{opacity:0;transform:translateY(-8px)}}@media(prefers-reduced-motion:reduce){.strct-cmdp__backdrop,.strct-cmdp{animation:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctKbd, selector: "strct-kbd" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
4487
|
+
`, isInline: true, styles: [".strct-cmdp__backdrop{position:fixed;inset:0;z-index:1200;display:flex;justify-content:center;align-items:flex-start;padding:14vh 16px 16px;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:strct-cmdp-fade .12s ease}.strct-cmdp{width:100%;max-width:560px;max-height:60vh;display:flex;flex-direction:column;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-xl);box-shadow:var(--shadow-floating);overflow:hidden;animation:strct-cmdp-rise .14s ease}.strct-cmdp__search{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--b1);color:var(--t3)}.strct-cmdp__input{flex:1;min-width:0;border:0;background:transparent;color:var(--t1);font-size:15px;font-family:var(--font);outline:none}.strct-cmdp__list{overflow-y:auto;padding:6px}.strct-cmdp__item{display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;border:0;border-radius:var(--radius-md);background:transparent;cursor:pointer;text-align:start;font-family:var(--font)}.strct-cmdp__item.is-active{background:var(--acc-m)}.strct-cmdp__icon{color:var(--t2);flex-shrink:0}.strct-cmdp__item.is-active .strct-cmdp__icon{color:var(--acc)}.strct-cmdp__label{flex:1;min-width:0;font-size:var(--text-lg);color:var(--t1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-cmdp__group{font-size:var(--text-sm);color:var(--t3);flex-shrink:0}.strct-cmdp__loading{display:flex;align-items:center;gap:9px;padding:10px 12px;font-size:var(--text-md);color:var(--t3)}.strct-cmdp__spin{width:13px;height:13px;border:2px solid var(--acc30);border-top-color:var(--acc);border-radius:50%;flex-shrink:0}@media(prefers-reduced-motion:no-preference){.strct-cmdp__spin{animation:strct-cmdp-spin .8s linear infinite}}@keyframes strct-cmdp-spin{to{transform:rotate(360deg)}}.strct-cmdp__empty{padding:20px;text-align:center;font-size:var(--text-md);color:var(--t3)}@keyframes strct-cmdp-fade{0%{opacity:0}}@keyframes strct-cmdp-rise{0%{opacity:0;transform:translateY(-8px)}}@media(prefers-reduced-motion:reduce){.strct-cmdp__backdrop,.strct-cmdp{animation:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctKbd, selector: "strct-kbd" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
4317
4488
|
}
|
|
4318
4489
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCommandPalette, decorators: [{
|
|
4319
4490
|
type: Component,
|
|
@@ -4348,6 +4519,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
4348
4519
|
<strct-kbd>Esc</strct-kbd>
|
|
4349
4520
|
</div>
|
|
4350
4521
|
<div class="strct-cmdp__list" role="listbox" [id]="listId">
|
|
4522
|
+
@if (loading()) {
|
|
4523
|
+
<div class="strct-cmdp__loading" aria-live="polite">
|
|
4524
|
+
<span class="strct-cmdp__spin" aria-hidden="true"></span>{{ loadingText() }}
|
|
4525
|
+
</div>
|
|
4526
|
+
}
|
|
4351
4527
|
@for (item of filtered(); track item.id; let i = $index) {
|
|
4352
4528
|
<button
|
|
4353
4529
|
type="button"
|
|
@@ -4377,14 +4553,509 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
4377
4553
|
}
|
|
4378
4554
|
</button>
|
|
4379
4555
|
} @empty {
|
|
4380
|
-
|
|
4556
|
+
@if (!loading()) {
|
|
4557
|
+
<div class="strct-cmdp__empty">{{ emptyText() }} “{{ query() }}”</div>
|
|
4558
|
+
}
|
|
4381
4559
|
}
|
|
4382
4560
|
</div>
|
|
4383
4561
|
</div>
|
|
4384
4562
|
</div>
|
|
4385
4563
|
}
|
|
4386
|
-
`, styles: [".strct-cmdp__backdrop{position:fixed;inset:0;z-index:1200;display:flex;justify-content:center;align-items:flex-start;padding:14vh 16px 16px;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:strct-cmdp-fade .12s ease}.strct-cmdp{width:100%;max-width:560px;max-height:60vh;display:flex;flex-direction:column;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-xl);box-shadow:var(--shadow-floating);overflow:hidden;animation:strct-cmdp-rise .14s ease}.strct-cmdp__search{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--b1);color:var(--t3)}.strct-cmdp__input{flex:1;min-width:0;border:0;background:transparent;color:var(--t1);font-size:15px;font-family:var(--font);outline:none}.strct-cmdp__list{overflow-y:auto;padding:6px}.strct-cmdp__item{display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;border:0;border-radius:var(--radius-md);background:transparent;cursor:pointer;text-align:start;font-family:var(--font)}.strct-cmdp__item.is-active{background:var(--acc-m)}.strct-cmdp__icon{color:var(--t2);flex-shrink:0}.strct-cmdp__item.is-active .strct-cmdp__icon{color:var(--acc)}.strct-cmdp__label{flex:1;min-width:0;font-size:var(--text-lg);color:var(--t1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-cmdp__group{font-size:var(--text-sm);color:var(--t3);flex-shrink:0}.strct-cmdp__empty{padding:20px;text-align:center;font-size:var(--text-md);color:var(--t3)}@keyframes strct-cmdp-fade{0%{opacity:0}}@keyframes strct-cmdp-rise{0%{opacity:0;transform:translateY(-8px)}}@media(prefers-reduced-motion:reduce){.strct-cmdp__backdrop,.strct-cmdp{animation:none}}\n"] }]
|
|
4387
|
-
}], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], hotkey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hotkey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], maxResults: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxResults", required: false }] }], picked: [{ type: i0.Output, args: ["picked"] }] } });
|
|
4564
|
+
`, styles: [".strct-cmdp__backdrop{position:fixed;inset:0;z-index:1200;display:flex;justify-content:center;align-items:flex-start;padding:14vh 16px 16px;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:strct-cmdp-fade .12s ease}.strct-cmdp{width:100%;max-width:560px;max-height:60vh;display:flex;flex-direction:column;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-xl);box-shadow:var(--shadow-floating);overflow:hidden;animation:strct-cmdp-rise .14s ease}.strct-cmdp__search{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--b1);color:var(--t3)}.strct-cmdp__input{flex:1;min-width:0;border:0;background:transparent;color:var(--t1);font-size:15px;font-family:var(--font);outline:none}.strct-cmdp__list{overflow-y:auto;padding:6px}.strct-cmdp__item{display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;border:0;border-radius:var(--radius-md);background:transparent;cursor:pointer;text-align:start;font-family:var(--font)}.strct-cmdp__item.is-active{background:var(--acc-m)}.strct-cmdp__icon{color:var(--t2);flex-shrink:0}.strct-cmdp__item.is-active .strct-cmdp__icon{color:var(--acc)}.strct-cmdp__label{flex:1;min-width:0;font-size:var(--text-lg);color:var(--t1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-cmdp__group{font-size:var(--text-sm);color:var(--t3);flex-shrink:0}.strct-cmdp__loading{display:flex;align-items:center;gap:9px;padding:10px 12px;font-size:var(--text-md);color:var(--t3)}.strct-cmdp__spin{width:13px;height:13px;border:2px solid var(--acc30);border-top-color:var(--acc);border-radius:50%;flex-shrink:0}@media(prefers-reduced-motion:no-preference){.strct-cmdp__spin{animation:strct-cmdp-spin .8s linear infinite}}@keyframes strct-cmdp-spin{to{transform:rotate(360deg)}}.strct-cmdp__empty{padding:20px;text-align:center;font-size:var(--text-md);color:var(--t3)}@keyframes strct-cmdp-fade{0%{opacity:0}}@keyframes strct-cmdp-rise{0%{opacity:0;transform:translateY(-8px)}}@media(prefers-reduced-motion:reduce){.strct-cmdp__backdrop,.strct-cmdp{animation:none}}\n"] }]
|
|
4565
|
+
}], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], hotkey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hotkey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], maxResults: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxResults", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], picked: [{ type: i0.Output, args: ["picked"] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }, { type: i0.Output, args: ["queryChange"] }] } });
|
|
4566
|
+
|
|
4567
|
+
/**
|
|
4568
|
+
* Compact search pill — the pattern from the docs header: a leading search
|
|
4569
|
+
* icon, a label / input and an optional keyboard-hint chip. Two modes:
|
|
4570
|
+
*
|
|
4571
|
+
* - **input** (default): a real search field. Two-way `value` (also
|
|
4572
|
+
* CVA-compatible), `(search)` emits on Enter, Escape / the × clears.
|
|
4573
|
+
*
|
|
4574
|
+
* <strct-searchbox [(value)]="q" (search)="run($event)" />
|
|
4575
|
+
*
|
|
4576
|
+
* - **`trigger`**: a button that only announces activation — the classic
|
|
4577
|
+
* "fake search that opens the command palette" header pattern:
|
|
4578
|
+
*
|
|
4579
|
+
* <strct-searchbox trigger hint="⌘K" (activated)="palette.open.set(true)" />
|
|
4580
|
+
*/
|
|
4581
|
+
class StrctSearchbox {
|
|
4582
|
+
host = inject(ElementRef);
|
|
4583
|
+
/** Placeholder text (and the trigger mode's label). */
|
|
4584
|
+
placeholder = input('Search', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
4585
|
+
/** Keyboard-hint chip (e.g. "⌘K"); hidden while typing. */
|
|
4586
|
+
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
4587
|
+
/** Render as an activation button instead of a real input (palette opener). */
|
|
4588
|
+
trigger = input(false, { ...(ngDevMode ? { debugName: "trigger" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4589
|
+
/** Show the × clear button when there is a value. */
|
|
4590
|
+
clearable = input(true, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4591
|
+
/** Accessible label; falls back to `placeholder`. */
|
|
4592
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4593
|
+
/** Accessible label of the clear button (localizable). */
|
|
4594
|
+
clearLabel = input('Clear search', ...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
|
|
4595
|
+
/** Current text (two-way; also drives the CVA value). */
|
|
4596
|
+
value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4597
|
+
/** Enter pressed in input mode — run the search with the current text. */
|
|
4598
|
+
search = output();
|
|
4599
|
+
/** Trigger mode clicked (open your command palette / search surface). */
|
|
4600
|
+
activated = output();
|
|
4601
|
+
focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
|
|
4602
|
+
isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
4603
|
+
onChange = () => { };
|
|
4604
|
+
onTouched = () => { };
|
|
4605
|
+
onInput(event) {
|
|
4606
|
+
const next = event.target.value;
|
|
4607
|
+
this.value.set(next);
|
|
4608
|
+
this.onChange(next);
|
|
4609
|
+
}
|
|
4610
|
+
/** Clear the text (Escape / the × button). */
|
|
4611
|
+
clear() {
|
|
4612
|
+
if (!this.value())
|
|
4613
|
+
return;
|
|
4614
|
+
this.value.set('');
|
|
4615
|
+
this.onChange('');
|
|
4616
|
+
}
|
|
4617
|
+
/** Focus the input programmatically (e.g. from a global shortcut). */
|
|
4618
|
+
focus() {
|
|
4619
|
+
this.host.nativeElement.querySelector('.strct-sb__input')?.focus();
|
|
4620
|
+
}
|
|
4621
|
+
writeValue(value) {
|
|
4622
|
+
this.value.set(value ?? '');
|
|
4623
|
+
}
|
|
4624
|
+
registerOnChange(fn) {
|
|
4625
|
+
this.onChange = fn;
|
|
4626
|
+
}
|
|
4627
|
+
registerOnTouched(fn) {
|
|
4628
|
+
this.onTouched = fn;
|
|
4629
|
+
}
|
|
4630
|
+
setDisabledState(isDisabled) {
|
|
4631
|
+
this.isDisabled.set(isDisabled);
|
|
4632
|
+
}
|
|
4633
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctSearchbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4634
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctSearchbox, isStandalone: true, selector: "strct-searchbox", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", activated: "activated" }, host: { classAttribute: "strct-sb-host" }, providers: [
|
|
4635
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StrctSearchbox), multi: true },
|
|
4636
|
+
], ngImport: i0, template: `
|
|
4637
|
+
@if (trigger()) {
|
|
4638
|
+
<button
|
|
4639
|
+
type="button"
|
|
4640
|
+
class="strct-sb strct-sb--trigger"
|
|
4641
|
+
[disabled]="isDisabled()"
|
|
4642
|
+
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
4643
|
+
(click)="activated.emit()"
|
|
4644
|
+
>
|
|
4645
|
+
<strct-icon class="strct-sb__icon" name="search" [size]="14" [strokeWidth]="1.6" />
|
|
4646
|
+
<span class="strct-sb__label">{{ placeholder() }}</span>
|
|
4647
|
+
@if (hint()) {
|
|
4648
|
+
<kbd class="strct-sb__kbd">{{ hint() }}</kbd>
|
|
4649
|
+
}
|
|
4650
|
+
</button>
|
|
4651
|
+
} @else {
|
|
4652
|
+
<div class="strct-sb" [class.strct-sb--disabled]="isDisabled()">
|
|
4653
|
+
<strct-icon class="strct-sb__icon" name="search" [size]="14" [strokeWidth]="1.6" />
|
|
4654
|
+
<input
|
|
4655
|
+
#field
|
|
4656
|
+
class="strct-sb__input"
|
|
4657
|
+
type="text"
|
|
4658
|
+
role="searchbox"
|
|
4659
|
+
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
4660
|
+
[placeholder]="placeholder()"
|
|
4661
|
+
[disabled]="isDisabled()"
|
|
4662
|
+
[value]="value()"
|
|
4663
|
+
(input)="onInput($event)"
|
|
4664
|
+
(keydown.enter)="search.emit(value())"
|
|
4665
|
+
(keydown.escape)="clear()"
|
|
4666
|
+
(focus)="focused.set(true)"
|
|
4667
|
+
(blur)="focused.set(false); onTouched()"
|
|
4668
|
+
/>
|
|
4669
|
+
@if (clearable() && value()) {
|
|
4670
|
+
<button
|
|
4671
|
+
type="button"
|
|
4672
|
+
class="strct-sb__clear"
|
|
4673
|
+
[attr.aria-label]="clearLabel()"
|
|
4674
|
+
(click)="clear(); field.focus()"
|
|
4675
|
+
>
|
|
4676
|
+
<strct-icon name="close" [size]="11" [strokeWidth]="1.8" />
|
|
4677
|
+
</button>
|
|
4678
|
+
} @else if (hint() && !focused()) {
|
|
4679
|
+
<kbd class="strct-sb__kbd">{{ hint() }}</kbd>
|
|
4680
|
+
}
|
|
4681
|
+
</div>
|
|
4682
|
+
}
|
|
4683
|
+
`, isInline: true, styles: [".strct-sb-host{display:inline-flex;max-width:100%}.strct-sb{display:inline-flex;align-items:center;gap:8px;width:100%;min-width:0;padding:5px 8px 5px 10px;border:1px solid var(--strct-sb-border, var(--b2));border-radius:7px;background:var(--strct-sb-bg, var(--bg-2));color:var(--strct-sb-fg, var(--t2));font-family:var(--font);font-size:var(--strct-sb-font-size, 12.5px);transition:background .14s ease,border-color .14s ease,color .14s ease}.strct-sb--trigger{cursor:pointer;text-align:start}.strct-sb--trigger:hover:not(:disabled){color:var(--strct-sb-fg-hover, var(--t1));background:var(--strct-sb-bg-hover, var(--bg-3))}.strct-sb--trigger:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-sb:focus-within:not(.strct-sb--trigger){border-color:var(--acc50);color:var(--t1)}.strct-sb--disabled,.strct-sb--trigger:disabled{opacity:.55;cursor:not-allowed}.strct-sb__icon{flex-shrink:0;color:var(--strct-sb-muted, var(--t3))}.strct-sb__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.strct-sb__input{flex:1;min-width:0;border:0;padding:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:12.5px;outline:none}.strct-sb__input::placeholder{color:var(--t3)}.strct-sb__kbd{flex-shrink:0;font-family:var(--mono);font-size:10px;line-height:1;padding:3px 5px;border:1px solid var(--strct-sb-kbd-border, var(--b2));border-radius:4px;background:var(--strct-sb-kbd-bg, var(--bg-2));color:var(--strct-sb-kbd-fg, var(--t3))}.strct-sb__clear{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin:-2px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer}.strct-sb__clear:hover{color:var(--t1);background:var(--bg-3)}.strct-sb__clear:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}@media(prefers-reduced-motion:reduce){.strct-sb{transition:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
4684
|
+
}
|
|
4685
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctSearchbox, decorators: [{
|
|
4686
|
+
type: Component,
|
|
4687
|
+
args: [{ selector: 'strct-searchbox', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], providers: [
|
|
4688
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StrctSearchbox), multi: true },
|
|
4689
|
+
], template: `
|
|
4690
|
+
@if (trigger()) {
|
|
4691
|
+
<button
|
|
4692
|
+
type="button"
|
|
4693
|
+
class="strct-sb strct-sb--trigger"
|
|
4694
|
+
[disabled]="isDisabled()"
|
|
4695
|
+
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
4696
|
+
(click)="activated.emit()"
|
|
4697
|
+
>
|
|
4698
|
+
<strct-icon class="strct-sb__icon" name="search" [size]="14" [strokeWidth]="1.6" />
|
|
4699
|
+
<span class="strct-sb__label">{{ placeholder() }}</span>
|
|
4700
|
+
@if (hint()) {
|
|
4701
|
+
<kbd class="strct-sb__kbd">{{ hint() }}</kbd>
|
|
4702
|
+
}
|
|
4703
|
+
</button>
|
|
4704
|
+
} @else {
|
|
4705
|
+
<div class="strct-sb" [class.strct-sb--disabled]="isDisabled()">
|
|
4706
|
+
<strct-icon class="strct-sb__icon" name="search" [size]="14" [strokeWidth]="1.6" />
|
|
4707
|
+
<input
|
|
4708
|
+
#field
|
|
4709
|
+
class="strct-sb__input"
|
|
4710
|
+
type="text"
|
|
4711
|
+
role="searchbox"
|
|
4712
|
+
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
4713
|
+
[placeholder]="placeholder()"
|
|
4714
|
+
[disabled]="isDisabled()"
|
|
4715
|
+
[value]="value()"
|
|
4716
|
+
(input)="onInput($event)"
|
|
4717
|
+
(keydown.enter)="search.emit(value())"
|
|
4718
|
+
(keydown.escape)="clear()"
|
|
4719
|
+
(focus)="focused.set(true)"
|
|
4720
|
+
(blur)="focused.set(false); onTouched()"
|
|
4721
|
+
/>
|
|
4722
|
+
@if (clearable() && value()) {
|
|
4723
|
+
<button
|
|
4724
|
+
type="button"
|
|
4725
|
+
class="strct-sb__clear"
|
|
4726
|
+
[attr.aria-label]="clearLabel()"
|
|
4727
|
+
(click)="clear(); field.focus()"
|
|
4728
|
+
>
|
|
4729
|
+
<strct-icon name="close" [size]="11" [strokeWidth]="1.8" />
|
|
4730
|
+
</button>
|
|
4731
|
+
} @else if (hint() && !focused()) {
|
|
4732
|
+
<kbd class="strct-sb__kbd">{{ hint() }}</kbd>
|
|
4733
|
+
}
|
|
4734
|
+
</div>
|
|
4735
|
+
}
|
|
4736
|
+
`, host: { class: 'strct-sb-host' }, styles: [".strct-sb-host{display:inline-flex;max-width:100%}.strct-sb{display:inline-flex;align-items:center;gap:8px;width:100%;min-width:0;padding:5px 8px 5px 10px;border:1px solid var(--strct-sb-border, var(--b2));border-radius:7px;background:var(--strct-sb-bg, var(--bg-2));color:var(--strct-sb-fg, var(--t2));font-family:var(--font);font-size:var(--strct-sb-font-size, 12.5px);transition:background .14s ease,border-color .14s ease,color .14s ease}.strct-sb--trigger{cursor:pointer;text-align:start}.strct-sb--trigger:hover:not(:disabled){color:var(--strct-sb-fg-hover, var(--t1));background:var(--strct-sb-bg-hover, var(--bg-3))}.strct-sb--trigger:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-sb:focus-within:not(.strct-sb--trigger){border-color:var(--acc50);color:var(--t1)}.strct-sb--disabled,.strct-sb--trigger:disabled{opacity:.55;cursor:not-allowed}.strct-sb__icon{flex-shrink:0;color:var(--strct-sb-muted, var(--t3))}.strct-sb__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.strct-sb__input{flex:1;min-width:0;border:0;padding:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:12.5px;outline:none}.strct-sb__input::placeholder{color:var(--t3)}.strct-sb__kbd{flex-shrink:0;font-family:var(--mono);font-size:10px;line-height:1;padding:3px 5px;border:1px solid var(--strct-sb-kbd-border, var(--b2));border-radius:4px;background:var(--strct-sb-kbd-bg, var(--bg-2));color:var(--strct-sb-kbd-fg, var(--t3))}.strct-sb__clear{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin:-2px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer}.strct-sb__clear:hover{color:var(--t1);background:var(--bg-3)}.strct-sb__clear:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}@media(prefers-reduced-motion:reduce){.strct-sb{transition:none}}\n"] }]
|
|
4737
|
+
}], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], activated: [{ type: i0.Output, args: ["activated"] }] } });
|
|
4738
|
+
|
|
4739
|
+
/**
|
|
4740
|
+
* Click-to-copy chip with built-in feedback: an icon (or icon + label) button
|
|
4741
|
+
* that writes `text` to the clipboard and flips to a ✓ "Copied" state for a
|
|
4742
|
+
* moment. The console workhorse for UUIDs, IPs, serials and snippets:
|
|
4743
|
+
*
|
|
4744
|
+
* <strct-copy text="172.16.75.100" />
|
|
4745
|
+
* <strct-copy [text]="host.id" label="Copy ID" />
|
|
4746
|
+
*/
|
|
4747
|
+
class StrctCopy {
|
|
4748
|
+
destroyRef = inject(DestroyRef);
|
|
4749
|
+
/** The text written to the clipboard. */
|
|
4750
|
+
text = input.required(...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
4751
|
+
/** Optional visible label next to the icon (icon-only by default). */
|
|
4752
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
4753
|
+
/** Accessible label; falls back to `copyLabel`. */
|
|
4754
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4755
|
+
/** Localizable strings. */
|
|
4756
|
+
copyLabel = input('Copy', ...(ngDevMode ? [{ debugName: "copyLabel" }] : /* istanbul ignore next */ []));
|
|
4757
|
+
copiedLabel = input('Copied', ...(ngDevMode ? [{ debugName: "copiedLabel" }] : /* istanbul ignore next */ []));
|
|
4758
|
+
/** Emitted after a successful copy, with the copied text. */
|
|
4759
|
+
copied = output();
|
|
4760
|
+
done = signal(false, ...(ngDevMode ? [{ debugName: "done" }] : /* istanbul ignore next */ []));
|
|
4761
|
+
timer = null;
|
|
4762
|
+
constructor() {
|
|
4763
|
+
this.destroyRef.onDestroy(() => {
|
|
4764
|
+
if (this.timer)
|
|
4765
|
+
clearTimeout(this.timer);
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
/** Write `text` to the clipboard and show the ✓ feedback. */
|
|
4769
|
+
copy() {
|
|
4770
|
+
const value = this.text();
|
|
4771
|
+
void navigator.clipboard?.writeText(value).then(() => {
|
|
4772
|
+
this.copied.emit(value);
|
|
4773
|
+
this.done.set(true);
|
|
4774
|
+
if (this.timer)
|
|
4775
|
+
clearTimeout(this.timer);
|
|
4776
|
+
this.timer = setTimeout(() => this.done.set(false), 1600);
|
|
4777
|
+
});
|
|
4778
|
+
}
|
|
4779
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCopy, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4780
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctCopy, isStandalone: true, selector: "strct-copy", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, copyLabel: { classPropertyName: "copyLabel", publicName: "copyLabel", isSignal: true, isRequired: false, transformFunction: null }, copiedLabel: { classPropertyName: "copiedLabel", publicName: "copiedLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { copied: "copied" }, host: { classAttribute: "strct-copy-host" }, ngImport: i0, template: `
|
|
4781
|
+
<button
|
|
4782
|
+
type="button"
|
|
4783
|
+
class="strct-copy"
|
|
4784
|
+
[class.strct-copy--done]="done()"
|
|
4785
|
+
[attr.aria-label]="done() ? copiedLabel() : ariaLabel() || copyLabel()"
|
|
4786
|
+
(click)="copy()"
|
|
4787
|
+
>
|
|
4788
|
+
<strct-icon [name]="done() ? 'check' : 'copy'" [size]="13" [strokeWidth]="1.6" />
|
|
4789
|
+
@if (label()) {
|
|
4790
|
+
<span class="strct-copy__label">{{ done() ? copiedLabel() : label() }}</span>
|
|
4791
|
+
}
|
|
4792
|
+
<span class="strct-copy__sr" aria-live="polite">{{ done() ? copiedLabel() : '' }}</span>
|
|
4793
|
+
</button>
|
|
4794
|
+
`, isInline: true, styles: [".strct-copy-host{display:inline-flex}.strct-copy{display:inline-flex;align-items:center;gap:5px;padding:3px 6px;border:0;border-radius:5px;background:transparent;color:var(--t3);font-family:var(--font);font-size:12px;cursor:pointer;transition:color .13s ease,background .13s ease}.strct-copy:hover{color:var(--t1);background:var(--bg-3)}.strct-copy:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-copy--done,.strct-copy--done:hover{color:var(--success)}.strct-copy__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.strct-copy{transition:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
4795
|
+
}
|
|
4796
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCopy, decorators: [{
|
|
4797
|
+
type: Component,
|
|
4798
|
+
args: [{ selector: 'strct-copy', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
|
|
4799
|
+
<button
|
|
4800
|
+
type="button"
|
|
4801
|
+
class="strct-copy"
|
|
4802
|
+
[class.strct-copy--done]="done()"
|
|
4803
|
+
[attr.aria-label]="done() ? copiedLabel() : ariaLabel() || copyLabel()"
|
|
4804
|
+
(click)="copy()"
|
|
4805
|
+
>
|
|
4806
|
+
<strct-icon [name]="done() ? 'check' : 'copy'" [size]="13" [strokeWidth]="1.6" />
|
|
4807
|
+
@if (label()) {
|
|
4808
|
+
<span class="strct-copy__label">{{ done() ? copiedLabel() : label() }}</span>
|
|
4809
|
+
}
|
|
4810
|
+
<span class="strct-copy__sr" aria-live="polite">{{ done() ? copiedLabel() : '' }}</span>
|
|
4811
|
+
</button>
|
|
4812
|
+
`, host: { class: 'strct-copy-host' }, styles: [".strct-copy-host{display:inline-flex}.strct-copy{display:inline-flex;align-items:center;gap:5px;padding:3px 6px;border:0;border-radius:5px;background:transparent;color:var(--t3);font-family:var(--font);font-size:12px;cursor:pointer;transition:color .13s ease,background .13s ease}.strct-copy:hover{color:var(--t1);background:var(--bg-3)}.strct-copy:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-copy--done,.strct-copy--done:hover{color:var(--success)}.strct-copy__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.strct-copy{transition:none}}\n"] }]
|
|
4813
|
+
}], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], copyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyLabel", required: false }] }], copiedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copiedLabel", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }] } });
|
|
4814
|
+
|
|
4815
|
+
/**
|
|
4816
|
+
* Copyable mono code / rendered-config block — the component form of the
|
|
4817
|
+
* `<details><pre>` pattern consoles keep hand-rolling. A header row carries an
|
|
4818
|
+
* optional title, a language tag, a copy button and (when `collapsible`) a
|
|
4819
|
+
* chevron; the body is a scrollable, keyboard-focusable `<pre>`.
|
|
4820
|
+
*
|
|
4821
|
+
* <strct-code [code]="yaml" language="yaml" title="cloud-init" collapsible />
|
|
4822
|
+
*/
|
|
4823
|
+
class StrctCode {
|
|
4824
|
+
/** The code / rendered config text (copied verbatim). */
|
|
4825
|
+
code = input.required(...(ngDevMode ? [{ debugName: "code" }] : /* istanbul ignore next */ []));
|
|
4826
|
+
/** Header title (e.g. a filename). */
|
|
4827
|
+
title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
4828
|
+
/** Small uppercase language tag (yaml, json, bash …). */
|
|
4829
|
+
language = input('', ...(ngDevMode ? [{ debugName: "language" }] : /* istanbul ignore next */ []));
|
|
4830
|
+
/** Show the copy button. */
|
|
4831
|
+
copyable = input(true, { ...(ngDevMode ? { debugName: "copyable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4832
|
+
/** Collapse to just the header (the `<details><pre>` pattern, done right). */
|
|
4833
|
+
collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4834
|
+
/** Collapsed state (two-way; only meaningful with `collapsible`). */
|
|
4835
|
+
collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
4836
|
+
/** Show a line-number gutter (numbers are never copied — `code` is). */
|
|
4837
|
+
lineNumbers = input(false, { ...(ngDevMode ? { debugName: "lineNumbers" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4838
|
+
/**
|
|
4839
|
+
* Soft-wrap long unbroken text (PEM/CSR blocks, base64, long commands) so a
|
|
4840
|
+
* dialog never scrolls horizontally. Wrapping breaks the gutter's visual
|
|
4841
|
+
* alignment, so `wrap` takes precedence and hides `lineNumbers`.
|
|
4842
|
+
*/
|
|
4843
|
+
wrap = input(false, { ...(ngDevMode ? { debugName: "wrap" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4844
|
+
/** Scroll the body past this height (px); null grows freely. */
|
|
4845
|
+
maxHeight = input(null, ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
|
|
4846
|
+
/** Localizable labels for the collapse toggle. */
|
|
4847
|
+
collapseLabel = input('Collapse', ...(ngDevMode ? [{ debugName: "collapseLabel" }] : /* istanbul ignore next */ []));
|
|
4848
|
+
expandLabel = input('Expand', ...(ngDevMode ? [{ debugName: "expandLabel" }] : /* istanbul ignore next */ []));
|
|
4849
|
+
lineNos = computed(() => {
|
|
4850
|
+
const n = this.code().split('\n').length;
|
|
4851
|
+
return Array.from({ length: n }, (_, i) => i + 1);
|
|
4852
|
+
}, ...(ngDevMode ? [{ debugName: "lineNos" }] : /* istanbul ignore next */ []));
|
|
4853
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCode, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4854
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctCode, isStandalone: true, selector: "strct-code", inputs: { code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, lineNumbers: { classPropertyName: "lineNumbers", publicName: "lineNumbers", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, collapseLabel: { classPropertyName: "collapseLabel", publicName: "collapseLabel", isSignal: true, isRequired: false, transformFunction: null }, expandLabel: { classPropertyName: "expandLabel", publicName: "expandLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, host: { properties: { "class.strct-code--collapsed": "collapsible() && collapsed()" }, classAttribute: "strct-code" }, ngImport: i0, template: `
|
|
4855
|
+
@if (title() || language() || copyable() || collapsible()) {
|
|
4856
|
+
<div class="strct-code__head">
|
|
4857
|
+
@if (collapsible()) {
|
|
4858
|
+
<button
|
|
4859
|
+
type="button"
|
|
4860
|
+
class="strct-code__toggle"
|
|
4861
|
+
[attr.aria-expanded]="!collapsed()"
|
|
4862
|
+
[attr.aria-label]="
|
|
4863
|
+
(collapsed() ? expandLabel() : collapseLabel()) +
|
|
4864
|
+
' ' +
|
|
4865
|
+
(title() || language() || 'code')
|
|
4866
|
+
"
|
|
4867
|
+
(click)="collapsed.set(!collapsed())"
|
|
4868
|
+
>
|
|
4869
|
+
<strct-icon
|
|
4870
|
+
name="chevronRight"
|
|
4871
|
+
[size]="12"
|
|
4872
|
+
[strokeWidth]="1.7"
|
|
4873
|
+
[class.strct-code__chev--open]="!collapsed()"
|
|
4874
|
+
/>
|
|
4875
|
+
</button>
|
|
4876
|
+
}
|
|
4877
|
+
@if (title()) {
|
|
4878
|
+
<span class="strct-code__title">{{ title() }}</span>
|
|
4879
|
+
}
|
|
4880
|
+
@if (language()) {
|
|
4881
|
+
<span class="strct-code__lang">{{ language() }}</span>
|
|
4882
|
+
}
|
|
4883
|
+
<span class="strct-code__grow"></span>
|
|
4884
|
+
@if (copyable()) {
|
|
4885
|
+
<strct-copy [text]="code()" />
|
|
4886
|
+
}
|
|
4887
|
+
</div>
|
|
4888
|
+
}
|
|
4889
|
+
@if (!collapsible() || !collapsed()) {
|
|
4890
|
+
<div
|
|
4891
|
+
class="strct-code__scroll"
|
|
4892
|
+
[class.strct-code__scroll--wrap]="wrap()"
|
|
4893
|
+
tabindex="0"
|
|
4894
|
+
role="region"
|
|
4895
|
+
[attr.aria-label]="title() || language() || 'code'"
|
|
4896
|
+
[style.max-height.px]="maxHeight()"
|
|
4897
|
+
>
|
|
4898
|
+
<pre class="strct-code__pre"><!--
|
|
4899
|
+
-->@if (lineNumbers() && !wrap()) {<!--
|
|
4900
|
+
--><span class="strct-code__gutter" aria-hidden="true">@for (n of lineNos(); track n) {<span class="strct-code__ln">{{ n }}</span>}</span><!--
|
|
4901
|
+
-->}<!--
|
|
4902
|
+
--><code class="strct-code__code">{{ code() }}</code></pre>
|
|
4903
|
+
</div>
|
|
4904
|
+
}
|
|
4905
|
+
`, isInline: true, styles: [".strct-code{display:block;border:1px solid var(--b2);border-radius:9px;background:var(--bg-2);overflow:hidden}.strct-code__head{display:flex;align-items:center;gap:8px;padding:6px 10px;border-bottom:1px solid var(--b1)}.strct-code--collapsed .strct-code__head{border-bottom:0}.strct-code__toggle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;margin:-3px 0 -3px -4px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer}.strct-code__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-code__toggle:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-code__toggle strct-icon{transition:transform .15s ease}.strct-code__chev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-code__toggle strct-icon{transition:none}}.strct-code__title{font-size:12.5px;font-weight:600;color:var(--t1)}.strct-code__lang{font-family:var(--mono);font-size:10px;letter-spacing:.6px;text-transform:uppercase;color:var(--t3);border:1px solid var(--b2);border-radius:4px;padding:2px 6px}.strct-code__grow{flex:1}.strct-code__scroll{overflow:auto}.strct-code__scroll:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-code__pre{display:flex;margin:0;padding:10px 12px;font-family:var(--mono);font-size:12px;line-height:1.55;color:var(--t1)}.strct-code__gutter{display:flex;flex-direction:column;flex-shrink:0;margin-inline-end:12px;padding-inline-end:10px;border-inline-end:1px solid var(--b1);color:var(--t3);text-align:end;-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums}.strct-code__code{display:block;white-space:pre}.strct-code__scroll--wrap .strct-code__code{white-space:pre-wrap;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "component", type: StrctCopy, selector: "strct-copy", inputs: ["text", "label", "ariaLabel", "copyLabel", "copiedLabel"], outputs: ["copied"] }, { kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
4906
|
+
}
|
|
4907
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCode, decorators: [{
|
|
4908
|
+
type: Component,
|
|
4909
|
+
args: [{ selector: 'strct-code', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctCopy, StrctIcon], template: `
|
|
4910
|
+
@if (title() || language() || copyable() || collapsible()) {
|
|
4911
|
+
<div class="strct-code__head">
|
|
4912
|
+
@if (collapsible()) {
|
|
4913
|
+
<button
|
|
4914
|
+
type="button"
|
|
4915
|
+
class="strct-code__toggle"
|
|
4916
|
+
[attr.aria-expanded]="!collapsed()"
|
|
4917
|
+
[attr.aria-label]="
|
|
4918
|
+
(collapsed() ? expandLabel() : collapseLabel()) +
|
|
4919
|
+
' ' +
|
|
4920
|
+
(title() || language() || 'code')
|
|
4921
|
+
"
|
|
4922
|
+
(click)="collapsed.set(!collapsed())"
|
|
4923
|
+
>
|
|
4924
|
+
<strct-icon
|
|
4925
|
+
name="chevronRight"
|
|
4926
|
+
[size]="12"
|
|
4927
|
+
[strokeWidth]="1.7"
|
|
4928
|
+
[class.strct-code__chev--open]="!collapsed()"
|
|
4929
|
+
/>
|
|
4930
|
+
</button>
|
|
4931
|
+
}
|
|
4932
|
+
@if (title()) {
|
|
4933
|
+
<span class="strct-code__title">{{ title() }}</span>
|
|
4934
|
+
}
|
|
4935
|
+
@if (language()) {
|
|
4936
|
+
<span class="strct-code__lang">{{ language() }}</span>
|
|
4937
|
+
}
|
|
4938
|
+
<span class="strct-code__grow"></span>
|
|
4939
|
+
@if (copyable()) {
|
|
4940
|
+
<strct-copy [text]="code()" />
|
|
4941
|
+
}
|
|
4942
|
+
</div>
|
|
4943
|
+
}
|
|
4944
|
+
@if (!collapsible() || !collapsed()) {
|
|
4945
|
+
<div
|
|
4946
|
+
class="strct-code__scroll"
|
|
4947
|
+
[class.strct-code__scroll--wrap]="wrap()"
|
|
4948
|
+
tabindex="0"
|
|
4949
|
+
role="region"
|
|
4950
|
+
[attr.aria-label]="title() || language() || 'code'"
|
|
4951
|
+
[style.max-height.px]="maxHeight()"
|
|
4952
|
+
>
|
|
4953
|
+
<pre class="strct-code__pre"><!--
|
|
4954
|
+
-->@if (lineNumbers() && !wrap()) {<!--
|
|
4955
|
+
--><span class="strct-code__gutter" aria-hidden="true">@for (n of lineNos(); track n) {<span class="strct-code__ln">{{ n }}</span>}</span><!--
|
|
4956
|
+
-->}<!--
|
|
4957
|
+
--><code class="strct-code__code">{{ code() }}</code></pre>
|
|
4958
|
+
</div>
|
|
4959
|
+
}
|
|
4960
|
+
`, host: {
|
|
4961
|
+
class: 'strct-code',
|
|
4962
|
+
'[class.strct-code--collapsed]': 'collapsible() && collapsed()',
|
|
4963
|
+
}, styles: [".strct-code{display:block;border:1px solid var(--b2);border-radius:9px;background:var(--bg-2);overflow:hidden}.strct-code__head{display:flex;align-items:center;gap:8px;padding:6px 10px;border-bottom:1px solid var(--b1)}.strct-code--collapsed .strct-code__head{border-bottom:0}.strct-code__toggle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;margin:-3px 0 -3px -4px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer}.strct-code__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-code__toggle:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-code__toggle strct-icon{transition:transform .15s ease}.strct-code__chev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-code__toggle strct-icon{transition:none}}.strct-code__title{font-size:12.5px;font-weight:600;color:var(--t1)}.strct-code__lang{font-family:var(--mono);font-size:10px;letter-spacing:.6px;text-transform:uppercase;color:var(--t3);border:1px solid var(--b2);border-radius:4px;padding:2px 6px}.strct-code__grow{flex:1}.strct-code__scroll{overflow:auto}.strct-code__scroll:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-code__pre{display:flex;margin:0;padding:10px 12px;font-family:var(--mono);font-size:12px;line-height:1.55;color:var(--t1)}.strct-code__gutter{display:flex;flex-direction:column;flex-shrink:0;margin-inline-end:12px;padding-inline-end:10px;border-inline-end:1px solid var(--b1);color:var(--t3);text-align:end;-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums}.strct-code__code{display:block;white-space:pre}.strct-code__scroll--wrap .strct-code__code{white-space:pre-wrap;overflow-wrap:anywhere}\n"] }]
|
|
4964
|
+
}], propDecorators: { code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], lineNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineNumbers", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], collapseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseLabel", required: false }] }], expandLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandLabel", required: false }] }] } });
|
|
4965
|
+
|
|
4966
|
+
/**
|
|
4967
|
+
* The standard strip above a data grid: a search field, the active filter
|
|
4968
|
+
* chips (each removable), a result count and a clear-all action. The bar owns
|
|
4969
|
+
* no filtering logic — it renders state and announces intent:
|
|
4970
|
+
*
|
|
4971
|
+
* <strct-filter-bar [(query)]="q" [filters]="chips" [count]="rows.length"
|
|
4972
|
+
* (removed)="drop($event)" (cleared)="reset()" />
|
|
4973
|
+
*/
|
|
4974
|
+
class StrctFilterBar {
|
|
4975
|
+
/** Search text (two-way; forwards to the embedded searchbox). */
|
|
4976
|
+
query = model('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
4977
|
+
/** Active filter chips. */
|
|
4978
|
+
filters = input([], ...(ngDevMode ? [{ debugName: "filters" }] : /* istanbul ignore next */ []));
|
|
4979
|
+
/** Result count shown at the end; null hides it. */
|
|
4980
|
+
count = input(null, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
|
|
4981
|
+
/** Localizable strings. */
|
|
4982
|
+
placeholder = input('Search', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
4983
|
+
countLabel = input('results', ...(ngDevMode ? [{ debugName: "countLabel" }] : /* istanbul ignore next */ []));
|
|
4984
|
+
clearLabel = input('Clear filters', ...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
|
|
4985
|
+
removeLabel = input('Remove filter', ...(ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []));
|
|
4986
|
+
/** Enter pressed in the search field. */
|
|
4987
|
+
search = output();
|
|
4988
|
+
/** A chip's × was clicked. */
|
|
4989
|
+
removed = output();
|
|
4990
|
+
/** "Clear filters" was clicked. */
|
|
4991
|
+
cleared = output();
|
|
4992
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctFilterBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4993
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctFilterBar, isStandalone: true, selector: "strct-filter-bar", inputs: { query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, countLabel: { classPropertyName: "countLabel", publicName: "countLabel", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { query: "queryChange", search: "search", removed: "removed", cleared: "cleared" }, host: { classAttribute: "strct-fb" }, ngImport: i0, template: `
|
|
4994
|
+
<strct-searchbox
|
|
4995
|
+
class="strct-fb__search"
|
|
4996
|
+
[placeholder]="placeholder()"
|
|
4997
|
+
[(value)]="query"
|
|
4998
|
+
(search)="search.emit($event)"
|
|
4999
|
+
/>
|
|
5000
|
+
@for (chip of filters(); track chip.id) {
|
|
5001
|
+
<span class="strct-fb__chip">
|
|
5002
|
+
<span class="strct-fb__chip-label">{{ chip.label }}</span>
|
|
5003
|
+
<button
|
|
5004
|
+
type="button"
|
|
5005
|
+
class="strct-fb__chip-x"
|
|
5006
|
+
[attr.aria-label]="removeLabel() + ' ' + chip.label"
|
|
5007
|
+
(click)="removed.emit(chip)"
|
|
5008
|
+
>
|
|
5009
|
+
<strct-icon name="close" [size]="10" [strokeWidth]="1.9" />
|
|
5010
|
+
</button>
|
|
5011
|
+
</span>
|
|
5012
|
+
}
|
|
5013
|
+
@if (filters().length > 1) {
|
|
5014
|
+
<button type="button" class="strct-fb__clear" (click)="cleared.emit()">
|
|
5015
|
+
{{ clearLabel() }}
|
|
5016
|
+
</button>
|
|
5017
|
+
}
|
|
5018
|
+
<ng-content />
|
|
5019
|
+
<span class="strct-fb__grow"></span>
|
|
5020
|
+
@if (count() !== null) {
|
|
5021
|
+
<span class="strct-fb__count" aria-live="polite">{{ count() }} {{ countLabel() }}</span>
|
|
5022
|
+
}
|
|
5023
|
+
`, isInline: true, styles: [".strct-fb{display:flex;align-items:center;gap:8px;flex-wrap:wrap;width:100%}.strct-fb__search{width:240px;max-width:100%}.strct-fb__chip{display:inline-flex;align-items:center;gap:5px;padding:3px 4px 3px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--acc-m);color:var(--acc);font-size:12px;font-weight:500}.strct-fb__chip-x{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.strct-fb__chip-x:hover{background:var(--acc30)}.strct-fb__chip-x:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-fb__clear{border:0;padding:3px 6px;border-radius:5px;background:transparent;color:var(--t3);font-family:var(--font);font-size:12px;cursor:pointer}.strct-fb__clear:hover{color:var(--t1);background:var(--bg-3)}.strct-fb__clear:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-fb__grow{flex:1}.strct-fb__count{font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums;white-space:nowrap}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctSearchbox, selector: "strct-searchbox", inputs: ["placeholder", "hint", "trigger", "clearable", "ariaLabel", "clearLabel", "value"], outputs: ["valueChange", "search", "activated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5024
|
+
}
|
|
5025
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctFilterBar, decorators: [{
|
|
5026
|
+
type: Component,
|
|
5027
|
+
args: [{ selector: 'strct-filter-bar', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon, StrctSearchbox], template: `
|
|
5028
|
+
<strct-searchbox
|
|
5029
|
+
class="strct-fb__search"
|
|
5030
|
+
[placeholder]="placeholder()"
|
|
5031
|
+
[(value)]="query"
|
|
5032
|
+
(search)="search.emit($event)"
|
|
5033
|
+
/>
|
|
5034
|
+
@for (chip of filters(); track chip.id) {
|
|
5035
|
+
<span class="strct-fb__chip">
|
|
5036
|
+
<span class="strct-fb__chip-label">{{ chip.label }}</span>
|
|
5037
|
+
<button
|
|
5038
|
+
type="button"
|
|
5039
|
+
class="strct-fb__chip-x"
|
|
5040
|
+
[attr.aria-label]="removeLabel() + ' ' + chip.label"
|
|
5041
|
+
(click)="removed.emit(chip)"
|
|
5042
|
+
>
|
|
5043
|
+
<strct-icon name="close" [size]="10" [strokeWidth]="1.9" />
|
|
5044
|
+
</button>
|
|
5045
|
+
</span>
|
|
5046
|
+
}
|
|
5047
|
+
@if (filters().length > 1) {
|
|
5048
|
+
<button type="button" class="strct-fb__clear" (click)="cleared.emit()">
|
|
5049
|
+
{{ clearLabel() }}
|
|
5050
|
+
</button>
|
|
5051
|
+
}
|
|
5052
|
+
<ng-content />
|
|
5053
|
+
<span class="strct-fb__grow"></span>
|
|
5054
|
+
@if (count() !== null) {
|
|
5055
|
+
<span class="strct-fb__count" aria-live="polite">{{ count() }} {{ countLabel() }}</span>
|
|
5056
|
+
}
|
|
5057
|
+
`, host: { class: 'strct-fb' }, styles: [".strct-fb{display:flex;align-items:center;gap:8px;flex-wrap:wrap;width:100%}.strct-fb__search{width:240px;max-width:100%}.strct-fb__chip{display:inline-flex;align-items:center;gap:5px;padding:3px 4px 3px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--acc-m);color:var(--acc);font-size:12px;font-weight:500}.strct-fb__chip-x{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.strct-fb__chip-x:hover{background:var(--acc30)}.strct-fb__chip-x:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-fb__clear{border:0;padding:3px 6px;border-radius:5px;background:transparent;color:var(--t3);font-family:var(--font);font-size:12px;cursor:pointer}.strct-fb__clear:hover{color:var(--t1);background:var(--bg-3)}.strct-fb__clear:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-fb__grow{flex:1}.strct-fb__count{font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums;white-space:nowrap}\n"] }]
|
|
5058
|
+
}], propDecorators: { query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }, { type: i0.Output, args: ["queryChange"] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], countLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], removed: [{ type: i0.Output, args: ["removed"] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
|
|
4388
5059
|
|
|
4389
5060
|
/** Tone class suffix per validation status (used by field + cell helper styles). */
|
|
4390
5061
|
function strctValidationTone(status) {
|
|
@@ -12097,5 +12768,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
12097
12768
|
* Generated bundle index. Do not edit.
|
|
12098
12769
|
*/
|
|
12099
12770
|
|
|
12100
|
-
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, StrctCellStatus, StrctChart, StrctCheckbox, StrctChips, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctContextMenu, StrctContextMenuTrigger, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDesc, StrctDescriptionList, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctEmptyState, StrctField, StrctFile, StrctFlow, StrctFooter, StrctGauge, StrctHeader, StrctHero, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLogin, StrctMenuPanel, StrctMenuService, StrctMetricTile, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRating, StrctRowDetailDef, StrctSectionMenu, StrctSegmented, StrctShell, StrctShellService, 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, strctValidationIcon, strctValidationTone };
|
|
12771
|
+
export { STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_PALETTES, STRCT_RAW_ICONS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCascadeHost, StrctCascadeNode, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChart, StrctCheckbox, StrctChips, StrctCode, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctContextMenu, StrctContextMenuTrigger, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDesc, StrctDescriptionList, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctEmptyState, StrctField, StrctFile, StrctFilterBar, StrctFlow, StrctFooter, StrctGauge, StrctHeader, StrctHero, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLogin, StrctMenuPanel, StrctMenuService, StrctMetricTile, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRating, StrctRowDetailDef, StrctSearchbox, StrctSectionMenu, StrctSegmented, StrctShell, StrctShellService, 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, strctValidationIcon, strctValidationTone };
|
|
12101
12772
|
//# sourceMappingURL=akcelik-strct.mjs.map
|