@kraft.stitch/angular 0.0.0-stage → 0.0.1
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/.github/workflows/npm-publish.yml +33 -0
- package/README.md +2 -3
- package/ng-package.json +10 -0
- package/package.json +27 -4
- package/src/lib/Buttons/button/button.ts +152 -0
- package/src/lib/Buttons/button-group/button-group.ts +129 -0
- package/src/lib/Buttons/chips/chips.ts +131 -0
- package/src/lib/Buttons/fab/fab.ts +96 -0
- package/src/lib/Calendars/datepicker/datepicker.ts +105 -0
- package/src/lib/Calendars/daterange/daterange.ts +112 -0
- package/src/lib/Calendars/timepicker/timepicker.ts +117 -0
- package/src/lib/Dropdowns/dropdown/dropdown.ts +121 -0
- package/src/lib/Dropdowns/listbox/listbox.ts +116 -0
- package/src/lib/Inputs/checkbox/checkbox.ts +126 -0
- package/src/lib/Inputs/colour-picker/colour-picker.ts +101 -0
- package/src/lib/Inputs/input/input.ts +139 -0
- package/src/lib/Inputs/otp-input/otp-input.ts +113 -0
- package/src/lib/Inputs/radio/radio.ts +115 -0
- package/src/lib/Inputs/rating/rating.ts +104 -0
- package/src/lib/Inputs/slider/slider.ts +121 -0
- package/src/lib/Inputs/switch/switch.ts +100 -0
- package/src/lib/Inputs/textarea/textarea.ts +129 -0
- package/src/lib/Inputs/theme-switch/theme-switch.ts +61 -0
- package/src/lib/Inputs/uploader/uploader.ts +97 -0
- package/src/lib/Layout/avatar/avatar.ts +66 -0
- package/src/lib/Layout/card/card.ts +88 -0
- package/src/lib/Layout/code-view/code-view.ts +77 -0
- package/src/lib/Layout/dialog/dialog.ts +80 -0
- package/src/lib/Layout/not-found/not-found.ts +73 -0
- package/src/lib/Layout/sidebar/sidebar.ts +97 -0
- package/src/lib/Layout/table/table.ts +81 -0
- package/src/lib/Layout/tooltip/tooltip.ts +48 -0
- package/src/lib/Navigation/accordion/accordion.ts +80 -0
- package/src/lib/Navigation/breadcrumb/breadcrumb.ts +58 -0
- package/src/lib/Navigation/carousel/carousel.ts +70 -0
- package/src/lib/Navigation/context-menu/context-menu.ts +79 -0
- package/src/lib/Navigation/nav-bar/nav-bar.ts +72 -0
- package/src/lib/Navigation/pagination/pagination.ts +82 -0
- package/src/lib/Navigation/stepper/stepper.ts +77 -0
- package/src/lib/Navigation/tab/tab.ts +71 -0
- package/src/lib/Navigation/toolbar/toolbar.ts +60 -0
- package/src/lib/Navigation/top-nav-bar/top-nav-bar.ts +71 -0
- package/src/lib/Navigation/tree-view/tree-view.ts +75 -0
- package/src/lib/Notification/badge/badge.ts +55 -0
- package/src/lib/Notification/loader/loader.ts +41 -0
- package/src/lib/Notification/message/message.ts +60 -0
- package/src/lib/Notification/progress/progress.ts +65 -0
- package/src/lib/Notification/spinner/spinner.ts +50 -0
- package/src/lib/Notification/toast/toast.ts +56 -0
- package/src/public-api.ts +62 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-card` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-card.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/** Mirrors the inline union used by `core/src/components/Layout/card/card.tsx`. */
|
|
8
|
+
export type KsCardVariant = 'default' | 'outlined' | 'elevated' | 'flat';
|
|
9
|
+
|
|
10
|
+
/** DOM shape of the real `<ks-card>` custom element, typed for the wrapper. */
|
|
11
|
+
type KsCardElement = HTMLElement & {
|
|
12
|
+
label: string;
|
|
13
|
+
cardTitle: string;
|
|
14
|
+
subtitle: string;
|
|
15
|
+
imageUrl: string;
|
|
16
|
+
imageAlt: string;
|
|
17
|
+
iconClass: string;
|
|
18
|
+
variant: KsCardVariant;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Angular wrapper around the `<ks-card>` Stencil custom element.
|
|
23
|
+
*
|
|
24
|
+
* ## Slots
|
|
25
|
+
* Unlike the Buttons/Calendars/Dropdowns wrappers, `<ks-card>` renders
|
|
26
|
+
* real, consumer-facing `<slot name="header" />`, `<slot />` (default) and
|
|
27
|
+
* `<slot name="footer" />` in its scoped (non-shadow) Stencil `render()`.
|
|
28
|
+
* `template: '<ng-content></ng-content>'` (a single unfiltered pass-through,
|
|
29
|
+
* not per-slot `<ng-content select="...">`) projects ALL of this Angular
|
|
30
|
+
* component's content as real light-DOM children of the `<ks-card>` host
|
|
31
|
+
* node; Stencil's own scoped-slot relocation logic (see `shadow: false,
|
|
32
|
+
* scoped: true` on the Stencil class) then places each child into the
|
|
33
|
+
* right native `<slot>` based on that child's own `slot="header"` /
|
|
34
|
+
* `slot="footer"` attribute — same as plain HTML, nothing Angular-specific
|
|
35
|
+
* needed beyond forwarding the projected content through untouched.
|
|
36
|
+
* Usage: `<ks-card><ks-header slot="header">…</ks-header>Body<ks-footer slot="footer">…</ks-footer></ks-card>`.
|
|
37
|
+
*/
|
|
38
|
+
@Component({
|
|
39
|
+
selector: 'ks-card',
|
|
40
|
+
standalone: true,
|
|
41
|
+
template: '<ng-content></ng-content>',
|
|
42
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
43
|
+
})
|
|
44
|
+
export class KsCardComponent {
|
|
45
|
+
private readonly el: KsCardElement;
|
|
46
|
+
|
|
47
|
+
/** Card title */
|
|
48
|
+
readonly label = input('');
|
|
49
|
+
|
|
50
|
+
/** Card title (Angular input name was `title`; aliased to avoid clashing with the native `title` attribute/tooltip). */
|
|
51
|
+
readonly cardTitle = input('');
|
|
52
|
+
|
|
53
|
+
/** Subtitle / subheading */
|
|
54
|
+
readonly subtitle = input('');
|
|
55
|
+
|
|
56
|
+
/** URL for header image */
|
|
57
|
+
readonly imageUrl = input('');
|
|
58
|
+
|
|
59
|
+
/** Alt text for image */
|
|
60
|
+
readonly imageAlt = input('');
|
|
61
|
+
|
|
62
|
+
/** SVG icon class */
|
|
63
|
+
readonly iconClass = input('');
|
|
64
|
+
|
|
65
|
+
/** Card visual variant */
|
|
66
|
+
readonly variant = input<KsCardVariant>('default');
|
|
67
|
+
|
|
68
|
+
/** Emits when clicked (only when clickable). From the native `cardClick` event. */
|
|
69
|
+
readonly ksCardClick = output<MouseEvent>();
|
|
70
|
+
|
|
71
|
+
constructor(ref: ElementRef<KsCardElement>) {
|
|
72
|
+
this.el = ref.nativeElement;
|
|
73
|
+
|
|
74
|
+
effect(() => (this.el.label = this.label()));
|
|
75
|
+
effect(() => (this.el.cardTitle = this.cardTitle()));
|
|
76
|
+
effect(() => (this.el.subtitle = this.subtitle()));
|
|
77
|
+
effect(() => (this.el.imageUrl = this.imageUrl()));
|
|
78
|
+
effect(() => (this.el.imageAlt = this.imageAlt()));
|
|
79
|
+
effect(() => (this.el.iconClass = this.iconClass()));
|
|
80
|
+
effect(() => (this.el.variant = this.variant()));
|
|
81
|
+
|
|
82
|
+
const onNativeCardClick = (event: Event): void => {
|
|
83
|
+
this.ksCardClick.emit((event as CustomEvent<MouseEvent>).detail);
|
|
84
|
+
};
|
|
85
|
+
this.el.addEventListener('cardClick', onNativeCardClick);
|
|
86
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('cardClick', onNativeCardClick));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-code-view` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-code-view.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, ElementRef, effect, input } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/** Mirrors the inline union used by `core/src/components/Layout/code-view/code-view.tsx`. */
|
|
8
|
+
export type KsCodeViewLanguage = 'html' | 'css' | 'typescript' | 'javascript' | 'json' | 'bash' | 'text';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mirrors `ActionButtonsConfig` exported by
|
|
12
|
+
* `core/src/components/Layout/code-view/code-view.tsx`. Duplicated
|
|
13
|
+
* locally — see the note in `Buttons/button-group/button-group.ts` for why.
|
|
14
|
+
*/
|
|
15
|
+
export interface ActionButtonsConfig {
|
|
16
|
+
expand?: boolean;
|
|
17
|
+
code?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** DOM shape of the real `<ks-code-view>` custom element, typed for the wrapper. */
|
|
21
|
+
type KsCodeViewElement = HTMLElement & {
|
|
22
|
+
code: string;
|
|
23
|
+
language: KsCodeViewLanguage;
|
|
24
|
+
label: string;
|
|
25
|
+
maxHeight: string;
|
|
26
|
+
showHeader: boolean;
|
|
27
|
+
collapsed: boolean;
|
|
28
|
+
actionButtonConfig: ActionButtonsConfig;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Angular wrapper around the `<ks-code-view>` Stencil custom element.
|
|
33
|
+
* Purely presentational — no events, no `<slot>` in the wrapped Stencil
|
|
34
|
+
* render, plain inputs only.
|
|
35
|
+
*/
|
|
36
|
+
@Component({
|
|
37
|
+
selector: 'ks-code-view',
|
|
38
|
+
standalone: true,
|
|
39
|
+
template: '',
|
|
40
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
41
|
+
})
|
|
42
|
+
export class KsCodeViewComponent {
|
|
43
|
+
private readonly el: KsCodeViewElement;
|
|
44
|
+
|
|
45
|
+
/** The source code string to display */
|
|
46
|
+
readonly code = input('');
|
|
47
|
+
|
|
48
|
+
/** Language hint shown in the header badge */
|
|
49
|
+
readonly language = input<KsCodeViewLanguage>('html');
|
|
50
|
+
|
|
51
|
+
/** Optional title shown in the header */
|
|
52
|
+
readonly label = input('');
|
|
53
|
+
|
|
54
|
+
/** Max visible height before scroll kicks in (CSS value, e.g. '20rem') */
|
|
55
|
+
readonly maxHeight = input('24rem');
|
|
56
|
+
|
|
57
|
+
/** Show the header bar with language badge & copy button */
|
|
58
|
+
readonly showHeader = input(true);
|
|
59
|
+
|
|
60
|
+
/** Whether the code block is currently collapsed */
|
|
61
|
+
readonly collapsed = input(false);
|
|
62
|
+
|
|
63
|
+
/** Whether the code block is to show and hide buttons */
|
|
64
|
+
readonly actionButtonConfig = input<ActionButtonsConfig>({ expand: true, code: true });
|
|
65
|
+
|
|
66
|
+
constructor(ref: ElementRef<KsCodeViewElement>) {
|
|
67
|
+
this.el = ref.nativeElement;
|
|
68
|
+
|
|
69
|
+
effect(() => (this.el.code = this.code()));
|
|
70
|
+
effect(() => (this.el.language = this.language()));
|
|
71
|
+
effect(() => (this.el.label = this.label()));
|
|
72
|
+
effect(() => (this.el.maxHeight = this.maxHeight()));
|
|
73
|
+
effect(() => (this.el.showHeader = this.showHeader()));
|
|
74
|
+
effect(() => (this.el.collapsed = this.collapsed()));
|
|
75
|
+
effect(() => (this.el.actionButtonConfig = this.actionButtonConfig()));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-dialog` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-dialog.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/** Mirrors the inline union used by `core/src/components/Layout/dialog/dialog.tsx`. */
|
|
8
|
+
export type KsDialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
9
|
+
|
|
10
|
+
/** DOM shape of the real `<ks-dialog>` custom element, typed for the wrapper. */
|
|
11
|
+
type KsDialogElement = HTMLElement & {
|
|
12
|
+
open: boolean;
|
|
13
|
+
label: string;
|
|
14
|
+
hideClose: boolean;
|
|
15
|
+
persistent: boolean;
|
|
16
|
+
size: KsDialogSize;
|
|
17
|
+
target: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Angular wrapper around the `<ks-dialog>` Stencil custom element.
|
|
22
|
+
*
|
|
23
|
+
* `open` is a plain input, not `ControlValueAccessor` — a dialog's open
|
|
24
|
+
* state isn't form data. For the same reason described throughout this
|
|
25
|
+
* repo, the output can't be named `openChange` (it would collide with the
|
|
26
|
+
* native `openChange`-style close event — see below), so bind explicitly:
|
|
27
|
+
* `[open]="isOpen" (ksCloseDialog)="isOpen = false"`.
|
|
28
|
+
*
|
|
29
|
+
* ## Slots
|
|
30
|
+
* `<ks-dialog>` renders real, consumer-facing `<slot />` (default) and
|
|
31
|
+
* `<slot name="footer" />`. Same single unfiltered `<ng-content>`
|
|
32
|
+
* pass-through as `Layout/card/card.ts` — see that file's doc comment for
|
|
33
|
+
* the full explanation of how native `slot="footer"` attributes on
|
|
34
|
+
* projected children route into the right Stencil `<slot>`.
|
|
35
|
+
*/
|
|
36
|
+
@Component({
|
|
37
|
+
selector: 'ks-dialog',
|
|
38
|
+
standalone: true,
|
|
39
|
+
template: '<ng-content></ng-content>',
|
|
40
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
41
|
+
})
|
|
42
|
+
export class KsDialogComponent {
|
|
43
|
+
private readonly el: KsDialogElement;
|
|
44
|
+
|
|
45
|
+
/** Whether the dialog is open */
|
|
46
|
+
readonly open = input(false);
|
|
47
|
+
|
|
48
|
+
/** Dialog title */
|
|
49
|
+
readonly label = input('');
|
|
50
|
+
|
|
51
|
+
/** Hide the built-in close (✕) button */
|
|
52
|
+
readonly hideClose = input(false);
|
|
53
|
+
|
|
54
|
+
/** Prevent closing by clicking the backdrop */
|
|
55
|
+
readonly persistent = input(false);
|
|
56
|
+
|
|
57
|
+
/** Panel size */
|
|
58
|
+
readonly size = input<KsDialogSize>('md');
|
|
59
|
+
|
|
60
|
+
/** Target element id (without #), or '' to use `<body>`. When set, the overlay covers only that element and the dialog is centred inside it. */
|
|
61
|
+
readonly target = input('');
|
|
62
|
+
|
|
63
|
+
/** Emits when the dialog requests close. From the native `closeDialog` event. */
|
|
64
|
+
readonly ksCloseDialog = output<void>();
|
|
65
|
+
|
|
66
|
+
constructor(ref: ElementRef<KsDialogElement>) {
|
|
67
|
+
this.el = ref.nativeElement;
|
|
68
|
+
|
|
69
|
+
effect(() => (this.el.open = this.open()));
|
|
70
|
+
effect(() => (this.el.label = this.label()));
|
|
71
|
+
effect(() => (this.el.hideClose = this.hideClose()));
|
|
72
|
+
effect(() => (this.el.persistent = this.persistent()));
|
|
73
|
+
effect(() => (this.el.size = this.size()));
|
|
74
|
+
effect(() => (this.el.target = this.target()));
|
|
75
|
+
|
|
76
|
+
const onNativeCloseDialog = (): void => this.ksCloseDialog.emit();
|
|
77
|
+
this.el.addEventListener('closeDialog', onNativeCloseDialog);
|
|
78
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('closeDialog', onNativeCloseDialog));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-not-found` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-not-found.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/** DOM shape of the real `<ks-not-found>` custom element, typed for the wrapper. */
|
|
8
|
+
type KsNotFoundElement = HTMLElement & {
|
|
9
|
+
buttonLabel: string;
|
|
10
|
+
homeHref: string;
|
|
11
|
+
countdown: number;
|
|
12
|
+
showActionButtons: boolean;
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Angular wrapper around the `<ks-not-found>` Stencil custom element.
|
|
20
|
+
*
|
|
21
|
+
* `<ks-not-found>` renders a real, consumer-facing default `<slot />`.
|
|
22
|
+
* Same single unfiltered `<ng-content>` pass-through as
|
|
23
|
+
* `Layout/card/card.ts` — see that file's doc comment for the full
|
|
24
|
+
* explanation of how projected content routes into the Stencil `<slot>`.
|
|
25
|
+
*/
|
|
26
|
+
@Component({
|
|
27
|
+
selector: 'ks-not-found',
|
|
28
|
+
standalone: true,
|
|
29
|
+
template: '<ng-content></ng-content>',
|
|
30
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
31
|
+
})
|
|
32
|
+
export class KsNotFoundComponent {
|
|
33
|
+
private readonly el: KsNotFoundElement;
|
|
34
|
+
|
|
35
|
+
/** Label shown on the redirect button (default: "Go to Home") */
|
|
36
|
+
readonly buttonLabel = input('Go to Home');
|
|
37
|
+
|
|
38
|
+
/** href the button and auto-redirect navigate to (default: "./") */
|
|
39
|
+
readonly homeHref = input('./');
|
|
40
|
+
|
|
41
|
+
/** Seconds before auto-redirect fires. Set to `0` to disable the countdown. */
|
|
42
|
+
readonly countdown = input(30);
|
|
43
|
+
|
|
44
|
+
readonly showActionButtons = input(true);
|
|
45
|
+
|
|
46
|
+
/** Custom heading (default: "404") */
|
|
47
|
+
readonly code = input('404');
|
|
48
|
+
|
|
49
|
+
/** Custom sub-heading (default: "Page not found") */
|
|
50
|
+
readonly message = input('Page not found');
|
|
51
|
+
|
|
52
|
+
/** Custom description below the heading */
|
|
53
|
+
readonly description = input("The page you're looking for doesn't exist or has been moved.");
|
|
54
|
+
|
|
55
|
+
/** Emitted just before the auto-redirect navigates away. From the native `redirecting` event. */
|
|
56
|
+
readonly ksRedirecting = output<void>();
|
|
57
|
+
|
|
58
|
+
constructor(ref: ElementRef<KsNotFoundElement>) {
|
|
59
|
+
this.el = ref.nativeElement;
|
|
60
|
+
|
|
61
|
+
effect(() => (this.el.buttonLabel = this.buttonLabel()));
|
|
62
|
+
effect(() => (this.el.homeHref = this.homeHref()));
|
|
63
|
+
effect(() => (this.el.countdown = this.countdown()));
|
|
64
|
+
effect(() => (this.el.showActionButtons = this.showActionButtons()));
|
|
65
|
+
effect(() => (this.el.code = this.code()));
|
|
66
|
+
effect(() => (this.el.message = this.message()));
|
|
67
|
+
effect(() => (this.el.description = this.description()));
|
|
68
|
+
|
|
69
|
+
const onNativeRedirecting = (): void => this.ksRedirecting.emit();
|
|
70
|
+
this.el.addEventListener('redirecting', onNativeRedirecting);
|
|
71
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('redirecting', onNativeRedirecting));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-sidebar` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-sidebar.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, model, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
export type KsSidebarPosition = 'left' | 'right';
|
|
8
|
+
export type KsSidebarType = 'over' | 'push';
|
|
9
|
+
|
|
10
|
+
/** DOM shape of the real `<ks-sidebar>` custom element, typed for the wrapper. */
|
|
11
|
+
type KsSidebarElement = HTMLElement & {
|
|
12
|
+
position: KsSidebarPosition;
|
|
13
|
+
type: KsSidebarType;
|
|
14
|
+
open: boolean;
|
|
15
|
+
width: string;
|
|
16
|
+
target: string;
|
|
17
|
+
openPanel(): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
toggle(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Angular wrapper around the `<ks-sidebar>` Stencil custom element.
|
|
24
|
+
*
|
|
25
|
+
* `open` is a plain input/output pair, not `ControlValueAccessor` — panel
|
|
26
|
+
* open state isn't form data. The native event is literally `openChange`,
|
|
27
|
+
* so (per the double-fire rule in `Buttons/button.ts`) the Angular output
|
|
28
|
+
* can't reuse that name; bind explicitly instead:
|
|
29
|
+
* `[open]="isOpen" (ksOpenChange)="isOpen = $event"`.
|
|
30
|
+
*
|
|
31
|
+
* `<ks-sidebar>`'s own `@Method()`s (`openPanel`/`close`/`toggle`) are
|
|
32
|
+
* exposed here as real passthrough methods — grab the component instance
|
|
33
|
+
* with `@ViewChild(KsSidebarComponent)` and call them directly.
|
|
34
|
+
*
|
|
35
|
+
* ## Slots
|
|
36
|
+
* Renders real `<slot name="header" />`, `<slot />` (default) and
|
|
37
|
+
* `<slot name="footer" />`. Same single unfiltered `<ng-content>`
|
|
38
|
+
* pass-through as `Layout/card/card.ts`.
|
|
39
|
+
*/
|
|
40
|
+
@Component({
|
|
41
|
+
selector: 'ks-sidebar',
|
|
42
|
+
standalone: true,
|
|
43
|
+
template: '<ng-content></ng-content>',
|
|
44
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
45
|
+
})
|
|
46
|
+
export class KsSidebarComponent {
|
|
47
|
+
private readonly el: KsSidebarElement;
|
|
48
|
+
|
|
49
|
+
/** Which side the panel appears on */
|
|
50
|
+
readonly position = input<KsSidebarPosition>('left');
|
|
51
|
+
|
|
52
|
+
/** Behaviour type */
|
|
53
|
+
readonly type = input<KsSidebarType>('over');
|
|
54
|
+
|
|
55
|
+
/** Initial/controlled open state. Bind explicitly: `[open]="isOpen" (ksOpenChange)="isOpen = $event"`. */
|
|
56
|
+
readonly open = model(false);
|
|
57
|
+
|
|
58
|
+
/** Width of the sidebar panel */
|
|
59
|
+
readonly width = input('18rem');
|
|
60
|
+
|
|
61
|
+
/** Target element id (without #) or '' to use the page. */
|
|
62
|
+
readonly target = input('');
|
|
63
|
+
|
|
64
|
+
/** Fires whenever `open` changes. From the native `openChange` event. */
|
|
65
|
+
readonly ksOpenChange = output<boolean>();
|
|
66
|
+
|
|
67
|
+
constructor(ref: ElementRef<KsSidebarElement>) {
|
|
68
|
+
this.el = ref.nativeElement;
|
|
69
|
+
|
|
70
|
+
effect(() => (this.el.position = this.position()));
|
|
71
|
+
effect(() => (this.el.type = this.type()));
|
|
72
|
+
effect(() => (this.el.open = this.open()));
|
|
73
|
+
effect(() => (this.el.width = this.width()));
|
|
74
|
+
effect(() => (this.el.target = this.target()));
|
|
75
|
+
|
|
76
|
+
const onNativeOpenChange = (event: Event): void => {
|
|
77
|
+
this.ksOpenChange.emit((event as CustomEvent<boolean>).detail);
|
|
78
|
+
};
|
|
79
|
+
this.el.addEventListener('openChange', onNativeOpenChange);
|
|
80
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('openChange', onNativeOpenChange));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Passthrough to `<ks-sidebar>`'s own `@Method() openPanel()`. */
|
|
84
|
+
openPanel(): Promise<void> {
|
|
85
|
+
return this.el.openPanel();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Passthrough to `<ks-sidebar>`'s own `@Method() close()`. */
|
|
89
|
+
close(): Promise<void> {
|
|
90
|
+
return this.el.close();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Passthrough to `<ks-sidebar>`'s own `@Method() toggle()`. */
|
|
94
|
+
toggle(): Promise<void> {
|
|
95
|
+
return this.el.toggle();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-table` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-table.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/** Mirrors the inline unions used by `core/src/components/Layout/table/table.tsx`. */
|
|
8
|
+
export type KsTableVariant = 'default' | 'striped' | 'bordered' | 'compact';
|
|
9
|
+
export type KsTableSize = 'sm' | 'md' | 'lg';
|
|
10
|
+
|
|
11
|
+
/** DOM shape of the real `<ks-table>` custom element, typed for the wrapper. */
|
|
12
|
+
type KsTableElement = HTMLElement & {
|
|
13
|
+
data: Record<string, unknown>[];
|
|
14
|
+
columns: string[];
|
|
15
|
+
columnLabels: Record<string, string>;
|
|
16
|
+
variant: KsTableVariant;
|
|
17
|
+
size: KsTableSize;
|
|
18
|
+
caption: string;
|
|
19
|
+
scrollable: boolean;
|
|
20
|
+
hoverable: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Angular wrapper around the `<ks-table>` Stencil custom element.
|
|
25
|
+
* No `<slot>` in the wrapped Stencil render — data-driven only.
|
|
26
|
+
*/
|
|
27
|
+
@Component({
|
|
28
|
+
selector: 'ks-table',
|
|
29
|
+
standalone: true,
|
|
30
|
+
template: '',
|
|
31
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
32
|
+
})
|
|
33
|
+
export class KsTableComponent {
|
|
34
|
+
private readonly el: KsTableElement;
|
|
35
|
+
|
|
36
|
+
/** Array of objects to render as table rows */
|
|
37
|
+
readonly data = input<Record<string, unknown>[]>([]);
|
|
38
|
+
|
|
39
|
+
/** Optional explicit column order; defaults to keys of the first row */
|
|
40
|
+
readonly columns = input<string[]>([]);
|
|
41
|
+
|
|
42
|
+
/** Optional custom column header labels: { key: 'Display Label' } */
|
|
43
|
+
readonly columnLabels = input<Record<string, string>>({});
|
|
44
|
+
|
|
45
|
+
/** Visual variant */
|
|
46
|
+
readonly variant = input<KsTableVariant>('default');
|
|
47
|
+
|
|
48
|
+
/** Row size */
|
|
49
|
+
readonly size = input<KsTableSize>('md');
|
|
50
|
+
|
|
51
|
+
/** Show a caption / title above the table */
|
|
52
|
+
readonly caption = input('');
|
|
53
|
+
|
|
54
|
+
/** Make the table horizontally scrollable on overflow */
|
|
55
|
+
readonly scrollable = input(true);
|
|
56
|
+
|
|
57
|
+
/** Highlight row on hover */
|
|
58
|
+
readonly hoverable = input(true);
|
|
59
|
+
|
|
60
|
+
/** Emits the clicked row's data. From the native `rowClick` event. */
|
|
61
|
+
readonly ksRowClick = output<Record<string, unknown>>();
|
|
62
|
+
|
|
63
|
+
constructor(ref: ElementRef<KsTableElement>) {
|
|
64
|
+
this.el = ref.nativeElement;
|
|
65
|
+
|
|
66
|
+
effect(() => (this.el.data = this.data()));
|
|
67
|
+
effect(() => (this.el.columns = this.columns()));
|
|
68
|
+
effect(() => (this.el.columnLabels = this.columnLabels()));
|
|
69
|
+
effect(() => (this.el.variant = this.variant()));
|
|
70
|
+
effect(() => (this.el.size = this.size()));
|
|
71
|
+
effect(() => (this.el.caption = this.caption()));
|
|
72
|
+
effect(() => (this.el.scrollable = this.scrollable()));
|
|
73
|
+
effect(() => (this.el.hoverable = this.hoverable()));
|
|
74
|
+
|
|
75
|
+
const onNativeRowClick = (event: Event): void => {
|
|
76
|
+
this.ksRowClick.emit((event as CustomEvent<Record<string, unknown>>).detail);
|
|
77
|
+
};
|
|
78
|
+
this.el.addEventListener('rowClick', onNativeRowClick);
|
|
79
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('rowClick', onNativeRowClick));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-tooltip` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-tooltip.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, ElementRef, effect, input } from '@angular/core';
|
|
6
|
+
import type { KsPosition } from '@kraft.stitch/core';
|
|
7
|
+
|
|
8
|
+
/** DOM shape of the real `<ks-tooltip>` custom element, typed for the wrapper. */
|
|
9
|
+
type KsTooltipElement = HTMLElement & {
|
|
10
|
+
text: string;
|
|
11
|
+
position: KsPosition;
|
|
12
|
+
visible: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Angular wrapper around the `<ks-tooltip>` Stencil custom element.
|
|
17
|
+
*
|
|
18
|
+
* `<ks-tooltip>` renders a real, consumer-facing default `<slot />` for
|
|
19
|
+
* the element the tooltip is attached to. Same single unfiltered
|
|
20
|
+
* `<ng-content>` pass-through as `Layout/card/card.ts` — wrap the
|
|
21
|
+
* trigger element as `<ks-tooltip text="Hi"><button>Hover me</button></ks-tooltip>`.
|
|
22
|
+
*/
|
|
23
|
+
@Component({
|
|
24
|
+
selector: 'ks-tooltip',
|
|
25
|
+
standalone: true,
|
|
26
|
+
template: '<ng-content></ng-content>',
|
|
27
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
28
|
+
})
|
|
29
|
+
export class KsTooltipComponent {
|
|
30
|
+
private readonly el: KsTooltipElement;
|
|
31
|
+
|
|
32
|
+
/** Tooltip text */
|
|
33
|
+
readonly text = input('');
|
|
34
|
+
|
|
35
|
+
/** Preferred placement */
|
|
36
|
+
readonly position = input<KsPosition>('top');
|
|
37
|
+
|
|
38
|
+
/** Show tooltip on demand (true = always visible) — leave false for hover behaviour */
|
|
39
|
+
readonly visible = input(false);
|
|
40
|
+
|
|
41
|
+
constructor(ref: ElementRef<KsTooltipElement>) {
|
|
42
|
+
this.el = ref.nativeElement;
|
|
43
|
+
|
|
44
|
+
effect(() => (this.el.text = this.text()));
|
|
45
|
+
effect(() => (this.el.position = this.position()));
|
|
46
|
+
effect(() => (this.el.visible = this.visible()));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Side-effect import: registers the `ks-accordion` tag — see
|
|
2
|
+
// `Buttons/button/button.ts` for the full explanation of this pattern.
|
|
3
|
+
import '@kraft.stitch/core/dist/components/ks-accordion.js';
|
|
4
|
+
|
|
5
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, effect, inject, input, output } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Mirrors `KsAccordionItem` exported by
|
|
9
|
+
* `core/src/components/Navigation/accordion/accordion.tsx`. Duplicated
|
|
10
|
+
* locally — see the note in `Buttons/button-group/button-group.ts` for why.
|
|
11
|
+
*/
|
|
12
|
+
export interface KsAccordionItem {
|
|
13
|
+
key: string;
|
|
14
|
+
label: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** DOM shape of the real `<ks-accordion>` custom element, typed for the wrapper. */
|
|
20
|
+
type KsAccordionElement = HTMLElement & {
|
|
21
|
+
items: KsAccordionItem[];
|
|
22
|
+
multi: boolean;
|
|
23
|
+
openKeys: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Angular wrapper around the `<ks-accordion>` Stencil custom element.
|
|
28
|
+
*
|
|
29
|
+
* ## Panel content — `ksPanel`, NOT a native `slot` attribute
|
|
30
|
+
* The Stencil source maps each light-DOM child carrying a `ksPanel="key"`
|
|
31
|
+
* attribute to a matching named `<slot>` internally (see
|
|
32
|
+
* `core/src/utils/ks-slots.util.ts`'s `getKsPanelMap()`) — it does NOT use
|
|
33
|
+
* the browser-native `slot="..."` attribute consumers would use for
|
|
34
|
+
* `Layout/card/card.ts`-style components. `template: '<ng-content></ng-content>'`
|
|
35
|
+
* still applies the same way: it's a single, unfiltered pass-through of
|
|
36
|
+
* whatever this Angular component's own template projects, as real
|
|
37
|
+
* light-DOM children of the `<ks-accordion>` host — the wrapper does no
|
|
38
|
+
* filtering itself, it just needs to exist so the projected content
|
|
39
|
+
* becomes real children for Stencil's `getKsPanelMap()` scan to find.
|
|
40
|
+
* Usage:
|
|
41
|
+
* `<ks-accordion [items]="items">
|
|
42
|
+
* <div ksPanel="profile">Profile body…</div>
|
|
43
|
+
* <div ksPanel="settings">Settings body…</div>
|
|
44
|
+
* </ks-accordion>`
|
|
45
|
+
*/
|
|
46
|
+
@Component({
|
|
47
|
+
selector: 'ks-accordion',
|
|
48
|
+
standalone: true,
|
|
49
|
+
template: '<ng-content></ng-content>',
|
|
50
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
51
|
+
})
|
|
52
|
+
export class KsAccordionComponent {
|
|
53
|
+
private readonly el: KsAccordionElement;
|
|
54
|
+
|
|
55
|
+
/** List of accordion panel definitions */
|
|
56
|
+
readonly items = input<KsAccordionItem[]>([]);
|
|
57
|
+
|
|
58
|
+
/** Allow multiple panels open simultaneously */
|
|
59
|
+
readonly multi = input(false);
|
|
60
|
+
|
|
61
|
+
/** Keys that should be open by default */
|
|
62
|
+
readonly openKeys = input<string[]>([]);
|
|
63
|
+
|
|
64
|
+
/** Emits the key that was toggled. From the native `toggled` event. */
|
|
65
|
+
readonly ksToggled = output<string>();
|
|
66
|
+
|
|
67
|
+
constructor(ref: ElementRef<KsAccordionElement>) {
|
|
68
|
+
this.el = ref.nativeElement;
|
|
69
|
+
|
|
70
|
+
effect(() => (this.el.items = this.items()));
|
|
71
|
+
effect(() => (this.el.multi = this.multi()));
|
|
72
|
+
effect(() => (this.el.openKeys = this.openKeys()));
|
|
73
|
+
|
|
74
|
+
const onNativeToggled = (event: Event): void => {
|
|
75
|
+
this.ksToggled.emit((event as CustomEvent<string>).detail);
|
|
76
|
+
};
|
|
77
|
+
this.el.addEventListener('toggled', onNativeToggled);
|
|
78
|
+
inject(DestroyRef).onDestroy(() => this.el.removeEventListener('toggled', onNativeToggled));
|
|
79
|
+
}
|
|
80
|
+
}
|