@kouji-ui/core 0.0.4 → 0.1.2

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.
Files changed (127) hide show
  1. package/fesm2022/kouji-ui-core.mjs +26789 -0
  2. package/fesm2022/kouji-ui-core.mjs.map +1 -0
  3. package/icon/icon.css +40 -0
  4. package/package.json +20 -9
  5. package/types/kouji-ui-core.d.ts +13545 -0
  6. package/typography/prose.css +312 -0
  7. package/.turbo/turbo-build.log +0 -27
  8. package/CHANGELOG.md +0 -21
  9. package/ng-package.json +0 -7
  10. package/src/a11y/.gitkeep +0 -0
  11. package/src/a11y/aria-describedby.spec.ts +0 -55
  12. package/src/a11y/aria-describedby.ts +0 -33
  13. package/src/a11y/focus-trap.spec.ts +0 -86
  14. package/src/a11y/focus-trap.ts +0 -70
  15. package/src/a11y/index.ts +0 -9
  16. package/src/a11y/live-region.spec.ts +0 -58
  17. package/src/a11y/live-region.ts +0 -48
  18. package/src/a11y/roving-tabindex.spec.ts +0 -62
  19. package/src/a11y/roving-tabindex.ts +0 -104
  20. package/src/a11y/visually-hidden.spec.ts +0 -34
  21. package/src/a11y/visually-hidden.ts +0 -24
  22. package/src/accordion/accordion.context.ts +0 -22
  23. package/src/accordion/accordion.spec.ts +0 -32
  24. package/src/accordion/accordion.ts +0 -129
  25. package/src/accordion/index.ts +0 -2
  26. package/src/avatar/avatar.spec.ts +0 -18
  27. package/src/avatar/avatar.ts +0 -26
  28. package/src/avatar/index.ts +0 -1
  29. package/src/badge/badge.spec.ts +0 -20
  30. package/src/badge/badge.ts +0 -14
  31. package/src/badge/index.ts +0 -1
  32. package/src/button/button.example.ts +0 -30
  33. package/src/button/button.finance.example.ts +0 -37
  34. package/src/button/button.retro.example.ts +0 -47
  35. package/src/button/button.sizes.example.ts +0 -25
  36. package/src/button/button.spec.ts +0 -32
  37. package/src/button/button.ts +0 -44
  38. package/src/button/index.ts +0 -1
  39. package/src/chart/chart.spec.ts +0 -27
  40. package/src/chart/chart.ts +0 -48
  41. package/src/chart/index.ts +0 -1
  42. package/src/checkbox/checkbox.spec.ts +0 -35
  43. package/src/checkbox/checkbox.ts +0 -82
  44. package/src/checkbox/index.ts +0 -1
  45. package/src/dialog/dialog.confirm.example.ts +0 -57
  46. package/src/dialog/dialog.context.ts +0 -11
  47. package/src/dialog/dialog.example.ts +0 -40
  48. package/src/dialog/dialog.finance.example.ts +0 -47
  49. package/src/dialog/dialog.retro.example.ts +0 -46
  50. package/src/dialog/dialog.service.ts +0 -176
  51. package/src/dialog/dialog.spec.ts +0 -122
  52. package/src/dialog/dialog.ts +0 -201
  53. package/src/dialog/index.ts +0 -9
  54. package/src/example-components.ts +0 -33
  55. package/src/form/form.context.ts +0 -3
  56. package/src/form/form.spec.ts +0 -25
  57. package/src/form/form.ts +0 -46
  58. package/src/form/index.ts +0 -2
  59. package/src/input/index.ts +0 -1
  60. package/src/input/input.spec.ts +0 -43
  61. package/src/input/input.ts +0 -80
  62. package/src/menu/index.ts +0 -2
  63. package/src/menu/menu.context.ts +0 -10
  64. package/src/menu/menu.example.ts +0 -39
  65. package/src/menu/menu.spec.ts +0 -168
  66. package/src/menu/menu.ts +0 -176
  67. package/src/popover/index.ts +0 -12
  68. package/src/popover/popover.context.ts +0 -18
  69. package/src/popover/popover.example.ts +0 -50
  70. package/src/popover/popover.finance.example.ts +0 -52
  71. package/src/popover/popover.retro.example.ts +0 -53
  72. package/src/popover/popover.spec.ts +0 -52
  73. package/src/popover/popover.ts +0 -124
  74. package/src/primitives/.gitkeep +0 -0
  75. package/src/primitives/forms/form-control.spec.ts +0 -59
  76. package/src/primitives/forms/form-control.ts +0 -90
  77. package/src/primitives/forms/index.ts +0 -1
  78. package/src/primitives/index.ts +0 -3
  79. package/src/primitives/interaction/disabled.spec.ts +0 -47
  80. package/src/primitives/interaction/disabled.ts +0 -24
  81. package/src/primitives/interaction/focus-ring.spec.ts +0 -62
  82. package/src/primitives/interaction/focus-ring.ts +0 -57
  83. package/src/primitives/interaction/index.ts +0 -2
  84. package/src/primitives/overlay/index.ts +0 -1
  85. package/src/primitives/overlay/overlay.spec.ts +0 -90
  86. package/src/primitives/overlay/overlay.ts +0 -106
  87. package/src/public-api.ts +0 -35
  88. package/src/radio/index.ts +0 -2
  89. package/src/radio/radio.context.ts +0 -3
  90. package/src/radio/radio.spec.ts +0 -36
  91. package/src/radio/radio.ts +0 -55
  92. package/src/select/index.ts +0 -2
  93. package/src/select/select.context.ts +0 -3
  94. package/src/select/select.spec.ts +0 -145
  95. package/src/select/select.ts +0 -250
  96. package/src/styles/docs-themes.css +0 -82
  97. package/src/table/index.ts +0 -1
  98. package/src/table/table.spec.ts +0 -113
  99. package/src/table/table.ts +0 -126
  100. package/src/tabs/index.ts +0 -2
  101. package/src/tabs/tabs.context.ts +0 -9
  102. package/src/tabs/tabs.spec.ts +0 -40
  103. package/src/tabs/tabs.ts +0 -130
  104. package/src/test-setup.ts +0 -5
  105. package/src/toast/index.ts +0 -21
  106. package/src/toast/toast.example.ts +0 -134
  107. package/src/toast/toast.finance.example.ts +0 -68
  108. package/src/toast/toast.retro.example.ts +0 -69
  109. package/src/toast/toast.service.ts +0 -153
  110. package/src/toast/toast.spec.ts +0 -95
  111. package/src/toast/toast.strategy.ts +0 -102
  112. package/src/toast/toast.ts +0 -307
  113. package/src/toast/toast.types.ts +0 -5
  114. package/src/toggle/index.ts +0 -1
  115. package/src/toggle/toggle.spec.ts +0 -24
  116. package/src/toggle/toggle.ts +0 -40
  117. package/src/tooltip/index.ts +0 -5
  118. package/src/tooltip/tooltip.example.ts +0 -46
  119. package/src/tooltip/tooltip.finance.example.ts +0 -54
  120. package/src/tooltip/tooltip.placements.example.ts +0 -56
  121. package/src/tooltip/tooltip.retro.example.ts +0 -65
  122. package/src/tooltip/tooltip.spec.ts +0 -104
  123. package/src/tooltip/tooltip.ts +0 -129
  124. package/tsconfig.lib.json +0 -13
  125. package/tsconfig.lib.prod.json +0 -11
  126. package/tsconfig.spec.json +0 -10
  127. package/vite.config.ts +0 -19
@@ -1,307 +0,0 @@
1
- import {
2
- DestroyRef, Directive, ElementRef, TemplateRef,
3
- afterNextRender, computed, inject, input, signal,
4
- } from '@angular/core';
5
- import { KjToastService, KjToastTemplateContext, KjToastVariant } from './toast.service';
6
- import { KJ_TOAST_STRATEGY } from './toast.strategy';
7
- import { KjToastPositionX, KjToastPositionY } from './toast.types';
8
-
9
- export type { KjToastVariant, KjToastContext, KjToastOptions } from './toast.service';
10
- export type { KjToastPositionX, KjToastPositionY } from './toast.types';
11
-
12
- /**
13
- * Marks an element as a single toast item. Sets `role`, `aria-atomic`, `data-variant`,
14
- * `data-front`, and exposes positional state as CSS custom properties so client CSS
15
- * can drive stacking, scaling, or spread-out animations.
16
- *
17
- * **CSS variables exposed (when `[kjToastId]` is bound):**
18
- * - `--kj-toast-index` — 1-based position from the front (newest = 1)
19
- * - `--kj-toast-before` — number of toasts behind this one
20
- * - `--kj-toast-after` — number of toasts in front of this one
21
- * - `--kj-toast-height` — measured pixel height (kept in sync via ResizeObserver)
22
- *
23
- * @doc
24
- * @doc-example Basic
25
- * @doc-theme default
26
- * @doc-file toast.example.ts
27
- * @doc-theme retro
28
- * @doc-file toast.retro.example.ts
29
- * @doc-theme finance
30
- * @doc-file toast.finance.example.ts
31
- *
32
- * @example
33
- * ```html
34
- * <ng-template #defaultTpl let-ctx>
35
- * <div kjToast [kjToastVariant]="ctx.variant" [kjToastId]="ctx.id">
36
- * <span>{{ ctx.message }}</span>
37
- * <button (click)="ctx.dismiss()" aria-label="Dismiss">×</button>
38
- * </div>
39
- * </ng-template>
40
- * ```
41
- * @category Core/Overlays
42
- */
43
- @Directive({
44
- selector: '[kjToast]',
45
- standalone: true,
46
- host: {
47
- '[attr.role]': 'role()',
48
- '[attr.aria-atomic]': '"true"',
49
- '[attr.data-variant]': 'kjToastVariant()',
50
- '[attr.data-front]': 'isFront()',
51
- '[style.--kj-toast-index]': 'index()',
52
- '[style.--kj-toast-before]': 'before()',
53
- '[style.--kj-toast-after]': 'after()',
54
- '[style.--kj-toast-height]': 'heightPx()',
55
- },
56
- })
57
- export class KjToast {
58
- /** Visual / semantic variant. Defaults to `'default'`. */
59
- kjToastVariant = input<KjToastVariant>('default');
60
-
61
- /**
62
- * The toast id this element renders. Bind from the template context (`ctx.id`)
63
- * so the directive can compute its position in the live queue and expose
64
- * `--kj-toast-index`, `--kj-toast-before`, `--kj-toast-after`, and `data-front`.
65
- * Optional — without it, the toast still renders with role/variant only.
66
- */
67
- kjToastId = input<string | null>(null);
68
-
69
- private readonly svc = inject(KjToastService);
70
- private readonly el = inject(ElementRef<HTMLElement>);
71
- private readonly destroyRef = inject(DestroyRef);
72
- private readonly _height = signal<number>(0);
73
-
74
- readonly role = computed(() =>
75
- this.kjToastVariant() === 'destructive' ? 'alert' : 'status',
76
- );
77
-
78
- /** 1-based position from the front (1 = newest). `null` when no id is bound. */
79
- readonly index = computed<number | null>(() => {
80
- const id = this.kjToastId();
81
- if (!id) return null;
82
- const list = this.svc.toasts();
83
- const i = list.findIndex(t => t.id === id);
84
- return i >= 0 ? list.length - i : null;
85
- });
86
-
87
- /** Toasts older than this one (behind in the stack). */
88
- readonly before = computed<number | null>(() => {
89
- const idx = this.index();
90
- return idx == null ? null : idx - 1;
91
- });
92
-
93
- /** Toasts newer than this one (in front of it). */
94
- readonly after = computed<number | null>(() => {
95
- const idx = this.index();
96
- if (idx == null) return null;
97
- return this.svc.toasts().length - idx;
98
- });
99
-
100
- /** True when this is the front-most (newest) toast. */
101
- readonly isFront = computed(() => this.index() === 1);
102
-
103
- /** Measured height as a pixel string (`"42px"`). Empty when unmeasured. */
104
- readonly heightPx = computed(() => {
105
- const h = this._height();
106
- return h > 0 ? `${h}px` : '';
107
- });
108
-
109
- constructor() {
110
- afterNextRender(() => {
111
- const node = this.el.nativeElement;
112
- const measure = () => this._height.set(Math.round(node.getBoundingClientRect().height));
113
- measure();
114
- // Use border-box geometry via getBoundingClientRect — contentRect strips padding.
115
- const ro = new ResizeObserver(measure);
116
- ro.observe(node);
117
- this.destroyRef.onDestroy(() => ro.disconnect());
118
- });
119
- }
120
- }
121
-
122
- /** Per-toast renderable: the resolved template + the bound context to render with. */
123
- export interface KjToastRenderable<TData = unknown> {
124
- readonly id: string;
125
- readonly template: TemplateRef<KjToastTemplateContext<TData>> | null;
126
- readonly context: KjToastTemplateContext<TData>;
127
- }
128
-
129
- /**
130
- * Container for active toasts. Reads the queue from `KjToastService`, applies
131
- * a visibility cap (`kjToastMaxVisible`), and exposes layout state both as
132
- * inputs-driven CSS variables and `data-position-x` / `data-position-y`
133
- * attributes so client CSS can drive position, gap, stacking strategy, and
134
- * z-index.
135
- *
136
- * **CSS variables exposed on the host:**
137
- * - `--kj-toast-gap` — gap between stacked toasts
138
- * - `--kj-toast-z-index` — base z-index for toasts
139
- * - `--kj-toasts-count` — number of currently-rendered toasts
140
- * - `--kj-toast-front-height` — measured height of the front-most toast
141
- *
142
- * **Data attributes:**
143
- * - `data-position-x="start|center|end"`
144
- * - `data-position-y="top|bottom"`
145
- * - `data-expanded="true|false"`
146
- *
147
- * @example
148
- * ```html
149
- * <ol kjToastViewport
150
- * [kjToastDefaultTemplate]="defaultTpl"
151
- * [kjToastMaxVisible]="3"
152
- * [kjToastGap]="8"
153
- * kjToastPositionX="end"
154
- * kjToastPositionY="bottom"
155
- * #vp="kjToastViewport"
156
- * aria-label="Notifications">
157
- * @for (r of vp.renderable(); track r.id) {
158
- * <li><ng-container *ngTemplateOutlet="r.template; context: { $implicit: r.context }" /></li>
159
- * }
160
- * </ol>
161
- * ```
162
- * @category Core/Overlays
163
- */
164
- @Directive({
165
- selector: '[kjToastViewport]',
166
- standalone: true,
167
- exportAs: 'kjToastViewport',
168
- host: {
169
- 'role': 'region',
170
- '[attr.aria-live]': '"polite"',
171
- '[attr.aria-atomic]': '"false"',
172
- '[attr.aria-relevant]': '"additions removals"',
173
- '[attr.data-position-x]': 'kjToastPositionX()',
174
- '[attr.data-position-y]': 'kjToastPositionY()',
175
- '[attr.data-expanded]': 'expanded()',
176
- '[style.--kj-toast-gap]': 'gapPx()',
177
- '[style.--kj-toast-z-index]': 'kjToastBaseZIndex()',
178
- '[style.--kj-toasts-count]': 'renderable().length',
179
- '[style.--kj-toast-front-height]': 'frontHeightPx()',
180
- '(mouseenter)': 'onHover(true)',
181
- '(mouseleave)': 'onHover(false)',
182
- '(focusin)': 'onHover(true)',
183
- '(focusout)': 'onHover(false)',
184
- },
185
- })
186
- export class KjToastViewport {
187
- private readonly svc = inject(KjToastService);
188
- private readonly el = inject(ElementRef<HTMLElement>);
189
- private readonly destroyRef = inject(DestroyRef);
190
- private readonly strategy = inject(KJ_TOAST_STRATEGY);
191
-
192
- /** Default template used for string-based toasts. Per-call templates override this. */
193
- readonly kjToastDefaultTemplate = input<TemplateRef<KjToastTemplateContext> | null>(null);
194
-
195
- /** Cap on simultaneously-rendered toasts. Excess stays queued and surfaces as space frees. Defaults from the active `KjToastStrategy`. */
196
- readonly kjToastMaxVisible = input<number>(this.strategy.maxVisible);
197
-
198
- /** Pixel gap between stacked toasts — exposed as `--kj-toast-gap`. Defaults from the active `KjToastStrategy`. */
199
- readonly kjToastGap = input<number>(this.strategy.gap);
200
-
201
- /** Base `z-index` exposed as `--kj-toast-z-index`. Defaults from the active `KjToastStrategy`. */
202
- readonly kjToastBaseZIndex = input<number>(this.strategy.baseZIndex);
203
-
204
- /** Horizontal anchor — written as `data-position-x` for client CSS. Defaults from the active `KjToastStrategy`. */
205
- readonly kjToastPositionX = input<KjToastPositionX>(this.strategy.positionX);
206
-
207
- /** Vertical anchor — written as `data-position-y` for client CSS. Defaults from the active `KjToastStrategy`. */
208
- readonly kjToastPositionY = input<KjToastPositionY>(this.strategy.positionY);
209
-
210
- /**
211
- * Manual override for the spread-out state. When unset (`undefined`) and the active
212
- * strategy has `expandOnHover: true`, the viewport flips automatically on hover/focus.
213
- * Set explicitly to force one state or to drive expansion from custom logic.
214
- */
215
- readonly kjToastExpand = input<boolean | undefined>(undefined);
216
-
217
- private readonly _hovered = signal(false);
218
-
219
- /** Resolved expand state — explicit input wins; otherwise falls back to hover when the strategy enables it. */
220
- readonly expanded = computed(() => {
221
- const override = this.kjToastExpand();
222
- if (override !== undefined) return override;
223
- return this.strategy.expandOnHover && this._hovered();
224
- });
225
-
226
- protected onHover(state: boolean): void {
227
- if (this.strategy.expandOnHover) this._hovered.set(state);
228
- }
229
-
230
- /** Live list of queued toasts (uncapped). */
231
- readonly toasts = this.svc.toasts;
232
-
233
- /** Renderables — capped by `kjToastMaxVisible`, paired with a template + bound context. */
234
- readonly renderable = computed<KjToastRenderable[]>(() => {
235
- const fallback = this.kjToastDefaultTemplate();
236
- const cap = this.kjToastMaxVisible();
237
- const list = this.toasts();
238
- // Newest are at the END of the service array; the front of the stack is the
239
- // last item. When capping, drop the OLDEST first.
240
- const visible = Number.isFinite(cap) && list.length > cap
241
- ? list.slice(list.length - cap)
242
- : list;
243
- return visible.map(t => ({
244
- id: t.id,
245
- template: t.template ?? fallback,
246
- context: this.svc.contextFor(t),
247
- }));
248
- });
249
-
250
- private readonly _frontHeight = signal<number>(0);
251
-
252
- /** `--kj-toast-gap` value (`"8px"`). */
253
- readonly gapPx = computed(() => `${this.kjToastGap()}px`);
254
-
255
- /** `--kj-toast-front-height` value (`"42px"`) — the measured height of the front toast. */
256
- readonly frontHeightPx = computed(() => {
257
- const h = this._frontHeight();
258
- return h > 0 ? `${h}px` : '';
259
- });
260
-
261
- constructor() {
262
- afterNextRender(() => {
263
- const root = this.el.nativeElement as HTMLElement;
264
- const measure = () => {
265
- const front = root.querySelector('[kjToast][data-front="true"]') as HTMLElement | null;
266
- if (!front) { this._frontHeight.set(0); return; }
267
- this._frontHeight.set(Math.round(front.getBoundingClientRect().height));
268
- };
269
- measure();
270
- const ro = new ResizeObserver(measure);
271
- // Observe the viewport — children added/resized trigger remeasure.
272
- ro.observe(root);
273
- // MutationObserver picks up new children (data-front="true" added on a new toast).
274
- const mo = new MutationObserver(measure);
275
- mo.observe(root, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-front'] });
276
- this.destroyRef.onDestroy(() => { ro.disconnect(); mo.disconnect(); });
277
- });
278
- }
279
- }
280
-
281
- /**
282
- * Optional dismiss button. Use `(click)="ctx.dismiss()"` directly in the template
283
- * for new code — this directive is for cases where the dismiss target id is
284
- * known outside the template context.
285
- *
286
- * @example
287
- * ```html
288
- * <button [kjToastClose]="toast.id" aria-label="Dismiss">×</button>
289
- * ```
290
- * @category Core/Overlays
291
- */
292
- @Directive({
293
- selector: '[kjToastClose]',
294
- standalone: true,
295
- host: {
296
- '(click)': 'dismiss()',
297
- },
298
- })
299
- export class KjToastClose {
300
- private readonly svc = inject(KjToastService);
301
- /** The id of the toast to dismiss on click. */
302
- kjToastClose = input.required<string>();
303
-
304
- dismiss(): void {
305
- this.svc.dismiss(this.kjToastClose());
306
- }
307
- }
@@ -1,5 +0,0 @@
1
- /** Horizontal anchor exposed as `data-position-x`. */
2
- export type KjToastPositionX = 'start' | 'center' | 'end';
3
-
4
- /** Vertical anchor exposed as `data-position-y`. */
5
- export type KjToastPositionY = 'top' | 'bottom';
@@ -1 +0,0 @@
1
- export { KjToggle } from './toggle';
@@ -1,24 +0,0 @@
1
- import { render, fireEvent } from '@testing-library/angular';
2
- import { axe, toHaveNoViolations } from 'jest-axe';
3
- import { KjToggle } from './toggle';
4
-
5
- expect.extend(toHaveNoViolations);
6
-
7
- describe('KjToggle', () => {
8
- it('has aria-pressed=false by default', async () => {
9
- const { getByRole } = await render(`<button kjToggle>Bold</button>`, { imports: [KjToggle] });
10
- expect(getByRole('button')).toHaveAttribute('aria-pressed', 'false');
11
- });
12
-
13
- it('toggles aria-pressed on click', async () => {
14
- const { getByRole } = await render(`<button kjToggle>Bold</button>`, { imports: [KjToggle] });
15
- fireEvent.click(getByRole('button'));
16
- expect(getByRole('button')).toHaveAttribute('aria-pressed', 'true');
17
- expect(getByRole('button')).toHaveAttribute('data-pressed', '');
18
- });
19
-
20
- it('passes axe audit', async () => {
21
- const { container } = await render(`<button kjToggle>Bold</button>`, { imports: [KjToggle] });
22
- expect(await axe(container)).toHaveNoViolations();
23
- });
24
- });
@@ -1,40 +0,0 @@
1
- import { Directive, inject, model } from '@angular/core';
2
- import { KjDisabled, KjFocusRing, KjFormControl } from '../primitives';
3
-
4
- /**
5
- * Adds toggle (press) behavior with Angular forms integration.
6
- *
7
- * @example
8
- * ```html
9
- * <button kjToggle [(kjPressed)]="isBold" aria-label="Bold">B</button>
10
- * ```
11
- * @category Core/Inputs
12
- */
13
- @Directive({
14
- selector: '[kjToggle]',
15
- standalone: true,
16
- hostDirectives: [
17
- { directive: KjDisabled, inputs: ['kjDisabled'] },
18
- KjFocusRing,
19
- KjFormControl,
20
- ],
21
- host: {
22
- '[attr.aria-pressed]': 'kjPressed().toString()',
23
- '[attr.data-pressed]': 'kjPressed() ? "" : null',
24
- '(click)': 'toggle()',
25
- '(blur)': 'formCtrl.notifyTouched()',
26
- },
27
- })
28
- export class KjToggle {
29
- readonly formCtrl = inject(KjFormControl);
30
-
31
- /** Whether the toggle is pressed. Supports two-way binding. */
32
- kjPressed = model<boolean>(false);
33
-
34
- /** @internal */
35
- toggle(): void {
36
- const next = !this.kjPressed();
37
- this.kjPressed.set(next);
38
- this.formCtrl.notifyChange(next);
39
- }
40
- }
@@ -1,5 +0,0 @@
1
- export {
2
- KjTooltipContent,
3
- KjTooltipTrigger,
4
- type KjTooltipSide,
5
- } from './tooltip';
@@ -1,46 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { KjTooltipTrigger, KjTooltipContent } from './tooltip';
3
- import { KjButton } from '../button/button';
4
-
5
- @Component({
6
- selector: 'kj-example-tooltip-basic',
7
- standalone: true,
8
- imports: [KjTooltipTrigger, KjTooltipContent, KjButton],
9
- styleUrls: ['../styles/docs-themes.css'],
10
- styles: [`
11
- :host { display: flex; align-items: center; justify-content: center; padding: 5rem 3rem; background: var(--kj-bg); font-family: var(--kj-font); min-height: 180px; }
12
- .row { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center; justify-content: center; }
13
- .tip-wrap { position: relative; display: inline-block; }
14
- button[kjButton] { padding: 0.5rem 1.25rem; font-family: var(--kj-font); font-size: 0.875rem; border: var(--kj-btn-border); cursor: pointer; transition: var(--kj-transition); }
15
- [data-variant="default"] { background: var(--kj-accent); color: var(--kj-accent-on); }
16
- [data-variant="link"] { background: transparent; color: var(--kj-accent); border: none; padding: 0.5rem 0.5rem; text-decoration: none; text-underline-offset: 4px; }
17
- [data-variant="link"]:hover { text-decoration: underline; }
18
- [data-variant="ghost"] { background: transparent; color: var(--kj-btn-ghost-color); }
19
- [kjTooltipContent] {
20
- position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
21
- background: var(--kj-accent); color: var(--kj-accent-on); padding: 0.25rem 0.625rem; font-size: 0.75rem;
22
- white-space: nowrap; pointer-events: none; transition: var(--kj-transition);
23
- }
24
- [kjTooltipContent][hidden] { display: none; }
25
- [data-side="bottom"] { top: calc(100% + 8px); bottom: auto; }
26
- [data-side="left"] { right: calc(100% + 8px); left: auto; bottom: auto; top: 50%; transform: translateY(-50%); }
27
- [data-side="right"] { left: calc(100% + 8px); bottom: auto; top: 50%; transform: translateY(-50%); }
28
- `],
29
- template: `
30
- <div class="row">
31
- <div class="tip-wrap">
32
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tip1">Default</button>
33
- <span #tip1 kjTooltipContent>Copy to clipboard</span>
34
- </div>
35
- <div class="tip-wrap">
36
- <button kjButton [kjVariant]="'link'" [kjTooltipTrigger]="tip2">Link</button>
37
- <span #tip2 kjTooltipContent [kjTooltipSide]="'bottom'">Opens below</span>
38
- </div>
39
- <div class="tip-wrap">
40
- <button kjButton [kjVariant]="'ghost'" [kjTooltipTrigger]="tip3">Ghost</button>
41
- <span #tip3 kjTooltipContent [kjTooltipSide]="'right'">Opens right</span>
42
- </div>
43
- </div>
44
- `,
45
- })
46
- export class TooltipBasicExample {}
@@ -1,54 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { KjTooltipTrigger, KjTooltipContent } from './tooltip';
3
- import { KjButton } from '../button/button';
4
-
5
- @Component({
6
- selector: 'kj-example-tooltip-finance',
7
- standalone: true,
8
- imports: [KjTooltipTrigger, KjTooltipContent, KjButton],
9
- styleUrls: ['../styles/docs-themes.css'],
10
- styles: [`
11
- :host { display: flex; align-items: center; justify-content: center; padding: 5rem 3rem; background: var(--kj-bg); font-family: var(--kj-font); min-height: 180px; color: var(--kj-text); }
12
- .row { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center; justify-content: center; }
13
- .tip-wrap { position: relative; display: inline-block; }
14
- button[kjButton] {
15
- padding: 4px 15px; font-family: var(--kj-font); font-size: 14px; font-weight: 400;
16
- border: 1px solid transparent; border-radius: 6px; cursor: pointer;
17
- transition: var(--kj-transition); line-height: 1.5;
18
- }
19
- button[kjButton][data-variant="default"] { background: var(--kj-accent); color: var(--kj-accent-on); border-color: var(--kj-accent); }
20
- button[kjButton][data-variant="default"]:hover { background: #4096ff; border-color: #4096ff; }
21
- button[kjButton][data-variant="link"] { background: transparent; color: var(--kj-accent); border-color: transparent; }
22
- button[kjButton][data-variant="link"]:hover { color: #4096ff; }
23
- button[kjButton][data-variant="ghost"] { background: transparent; color: var(--kj-text); border: 1px solid var(--kj-text); }
24
- button[kjButton][data-variant="ghost"]:hover { color: var(--kj-accent); border-color: var(--kj-accent); }
25
- [kjTooltipContent] {
26
- position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
27
- background: #1f2937; color: #f9fafb;
28
- padding: 0.3rem 0.625rem; font-size: 0.75rem; border-radius: var(--kj-radius-sm);
29
- white-space: nowrap; pointer-events: none; box-shadow: var(--kj-shadow-sm);
30
- }
31
- [kjTooltipContent][hidden] { display: none; }
32
- [data-side="bottom"] { top: calc(100% + 8px); bottom: auto; }
33
- [data-side="right"] { left: calc(100% + 8px); bottom: auto; top: 50%; transform: translateY(-50%); }
34
- [data-side="left"] { right: calc(100% + 8px); left: auto; bottom: auto; top: 50%; transform: translateY(-50%); }
35
- `],
36
- host: { class: 'kj-theme-finance' },
37
- template: `
38
- <div class="row">
39
- <div class="tip-wrap">
40
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tip1">Default</button>
41
- <span #tip1 kjTooltipContent>Copy to clipboard</span>
42
- </div>
43
- <div class="tip-wrap">
44
- <button kjButton [kjVariant]="'link'" [kjTooltipTrigger]="tip2">Link</button>
45
- <span #tip2 kjTooltipContent [kjTooltipSide]="'bottom'">Opens below</span>
46
- </div>
47
- <div class="tip-wrap">
48
- <button kjButton [kjVariant]="'ghost'" [kjTooltipTrigger]="tip3">Ghost</button>
49
- <span #tip3 kjTooltipContent [kjTooltipSide]="'right'">Opens right</span>
50
- </div>
51
- </div>
52
- `,
53
- })
54
- export class TooltipFinanceExample {}
@@ -1,56 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { KjTooltipTrigger, KjTooltipContent } from './tooltip';
3
- import { KjButton } from '../button/button';
4
-
5
- @Component({
6
- selector: 'kj-example-tooltip-placements',
7
- standalone: true,
8
- imports: [KjTooltipTrigger, KjTooltipContent, KjButton],
9
- styleUrls: ['../styles/docs-themes.css'],
10
- styles: [`
11
- :host { display: flex; align-items: center; justify-content: center; padding: 5rem 4rem; background: var(--kj-bg); font-family: var(--kj-font); min-height: 220px; }
12
- .grid { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto auto auto; gap: 0.75rem; place-items: center; }
13
- .tip-wrap { position: relative; display: inline-block; }
14
- button[kjButton] { padding: 0.5rem 1.25rem; font-family: var(--kj-font); font-size: 0.875rem; border: var(--kj-btn-border); cursor: pointer; transition: var(--kj-transition); }
15
- [data-variant="default"] { background: var(--kj-accent); color: var(--kj-accent-on); }
16
- [kjTooltipContent] {
17
- position: absolute; z-index: 10;
18
- background: var(--kj-accent); color: var(--kj-accent-on); padding: 0.25rem 0.625rem; font-size: 0.75rem;
19
- white-space: nowrap; pointer-events: none; transition: var(--kj-transition);
20
- }
21
- [kjTooltipContent][hidden] { display: none; }
22
- [data-side="top"] { bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); }
23
- [data-side="bottom"] { top: calc(100% + 8px); left: 50%; transform: translateX(-50%); }
24
- [data-side="left"] { right: calc(100% + 8px); top: 50%; transform: translateY(-50%); }
25
- [data-side="right"] { left: calc(100% + 8px); top: 50%; transform: translateY(-50%); }
26
- .empty { width: 80px; }
27
- `],
28
- template: `
29
- <div class="grid">
30
- <div class="empty"></div>
31
- <div class="tip-wrap">
32
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tipTop">Top</button>
33
- <span #tipTop kjTooltipContent [kjTooltipSide]="'top'">Top tooltip</span>
34
- </div>
35
- <div class="empty"></div>
36
-
37
- <div class="tip-wrap">
38
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tipLeft">Left</button>
39
- <span #tipLeft kjTooltipContent [kjTooltipSide]="'left'">Left tooltip</span>
40
- </div>
41
- <div class="empty"></div>
42
- <div class="tip-wrap">
43
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tipRight">Right</button>
44
- <span #tipRight kjTooltipContent [kjTooltipSide]="'right'">Right tooltip</span>
45
- </div>
46
-
47
- <div class="empty"></div>
48
- <div class="tip-wrap">
49
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tipBottom">Bottom</button>
50
- <span #tipBottom kjTooltipContent [kjTooltipSide]="'bottom'">Bottom tooltip</span>
51
- </div>
52
- <div class="empty"></div>
53
- </div>
54
- `,
55
- })
56
- export class TooltipPlacementsExample {}
@@ -1,65 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { KjTooltipTrigger, KjTooltipContent } from './tooltip';
3
- import { KjButton } from '../button/button';
4
-
5
- @Component({
6
- selector: 'kj-example-tooltip-retro',
7
- standalone: true,
8
- imports: [KjTooltipTrigger, KjTooltipContent, KjButton],
9
- styleUrls: ['../styles/docs-themes.css'],
10
- styles: [`
11
- :host { display: flex; align-items: center; justify-content: center; padding: 5rem 3rem; background: var(--kj-bg); font-family: var(--kj-font); min-height: 180px; color: var(--kj-text); }
12
- .row { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center; justify-content: center; }
13
- .tip-wrap { position: relative; display: inline-block; }
14
- button[kjButton] {
15
- padding: 0.35rem 0.875rem; font-family: var(--kj-font); font-size: 0.75rem;
16
- font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
17
- border: var(--kj-btn-border); border-radius: var(--kj-radius-md); cursor: pointer;
18
- box-shadow: var(--kj-shadow-sm); transition: var(--kj-transition);
19
- }
20
- button[kjButton]:not([data-variant="link"]):not([data-variant="ghost"]):hover {
21
- transform: translate(-1px, -1px); box-shadow: var(--kj-shadow-md);
22
- }
23
- button[kjButton][data-variant="default"] { background: var(--kj-text); color: var(--kj-bg); }
24
- button[kjButton][data-variant="link"] {
25
- background: transparent; color: var(--kj-text); border: none; box-shadow: none;
26
- border-radius: 0; padding: 0.35rem 0.875rem; text-decoration: none;
27
- }
28
- button[kjButton][data-variant="link"]:hover {
29
- text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px;
30
- }
31
- button[kjButton][data-variant="ghost"] {
32
- background: transparent; color: var(--kj-text); border: 2px solid transparent; box-shadow: none; opacity: 0.7;
33
- }
34
- button[kjButton][data-variant="ghost"]:hover { opacity: 1; background: rgba(0,0,0,0.06); }
35
- [kjTooltipContent] {
36
- position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
37
- background: var(--kj-text); color: var(--kj-bg);
38
- padding: 0.2rem 0.5rem; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
39
- white-space: nowrap; pointer-events: none; border: 1px solid var(--kj-border);
40
- box-shadow: var(--kj-shadow-sm);
41
- }
42
- [kjTooltipContent][hidden] { display: none; }
43
- [data-side="bottom"] { top: calc(100% + 8px); bottom: auto; }
44
- [data-side="right"] { left: calc(100% + 8px); bottom: auto; top: 50%; transform: translateY(-50%); }
45
- [data-side="left"] { right: calc(100% + 8px); left: auto; bottom: auto; top: 50%; transform: translateY(-50%); }
46
- `],
47
- host: { class: 'kj-theme-retro' },
48
- template: `
49
- <div class="row">
50
- <div class="tip-wrap">
51
- <button kjButton [kjVariant]="'default'" [kjTooltipTrigger]="tip1">Default</button>
52
- <span #tip1 kjTooltipContent>Copy to clipboard</span>
53
- </div>
54
- <div class="tip-wrap">
55
- <button kjButton [kjVariant]="'link'" [kjTooltipTrigger]="tip2">Link</button>
56
- <span #tip2 kjTooltipContent [kjTooltipSide]="'bottom'">Opens below</span>
57
- </div>
58
- <div class="tip-wrap">
59
- <button kjButton [kjVariant]="'ghost'" [kjTooltipTrigger]="tip3">Ghost</button>
60
- <span #tip3 kjTooltipContent [kjTooltipSide]="'right'">Opens right</span>
61
- </div>
62
- </div>
63
- `,
64
- })
65
- export class TooltipRetroExample {}