@kanso-protocol/search-bar 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { KpButtonComponent } from '@kanso-protocol/button';
|
|
4
|
+
import { KpIconComponent } from '@kanso-protocol/icon';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Kanso Protocol — SearchBar
|
|
8
|
+
*
|
|
9
|
+
* Two variants:
|
|
10
|
+
* - `inline` — a simple search input with optional ⌘K shortcut hint,
|
|
11
|
+
* good for header search or page filters.
|
|
12
|
+
* - `command-palette` — a self-contained panel: input + grouped
|
|
13
|
+
* results with shortcuts + footer helper keys. Typically rendered
|
|
14
|
+
* inside a Dialog/Overlay.
|
|
15
|
+
*
|
|
16
|
+
* The component is presentational; filtering results, opening the
|
|
17
|
+
* palette via ⌘K, and committing selection are all consumer
|
|
18
|
+
* responsibilities.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <!-- Inline in Header -->
|
|
22
|
+
* <kp-search-bar variant="inline" size="md" [showShortcutHint]="true" (search)="onSearch($event)"/>
|
|
23
|
+
*
|
|
24
|
+
* <!-- Command Palette (inside a Dialog) -->
|
|
25
|
+
* <kp-search-bar variant="command-palette" [groups]="results" (itemClick)="run($event)"/>
|
|
26
|
+
*/
|
|
27
|
+
class KpSearchBarComponent {
|
|
28
|
+
variant = 'inline';
|
|
29
|
+
size = 'md';
|
|
30
|
+
placeholder = 'Search anything...';
|
|
31
|
+
value = '';
|
|
32
|
+
showShortcutHint = true;
|
|
33
|
+
shortcutHint = '⌘K';
|
|
34
|
+
/** Result groups — only rendered in `command-palette` variant */
|
|
35
|
+
groups = [];
|
|
36
|
+
valueChange = new EventEmitter();
|
|
37
|
+
search = new EventEmitter();
|
|
38
|
+
itemClick = new EventEmitter();
|
|
39
|
+
focused = false;
|
|
40
|
+
get hostClasses() {
|
|
41
|
+
return `kp-search-bar kp-search-bar--${this.variant} kp-search-bar--${this.size}`;
|
|
42
|
+
}
|
|
43
|
+
handleInput(event) {
|
|
44
|
+
const target = event.target;
|
|
45
|
+
this.value = target.value;
|
|
46
|
+
this.valueChange.emit(this.value);
|
|
47
|
+
this.search.emit(this.value);
|
|
48
|
+
}
|
|
49
|
+
clear() {
|
|
50
|
+
this.value = '';
|
|
51
|
+
this.valueChange.emit('');
|
|
52
|
+
this.search.emit('');
|
|
53
|
+
}
|
|
54
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpSearchBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
55
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: KpSearchBarComponent, isStandalone: true, selector: "kp-search-bar", inputs: { variant: "variant", size: "size", placeholder: "placeholder", value: "value", showShortcutHint: "showShortcutHint", shortcutHint: "shortcutHint", groups: "groups" }, outputs: { valueChange: "valueChange", search: "search", itemClick: "itemClick" }, host: { properties: { "class": "hostClasses" } }, ngImport: i0, template: `
|
|
56
|
+
@if (variant === 'inline') {
|
|
57
|
+
<div class="kp-search-bar__wrap" [class.kp-search-bar__wrap--focused]="focused">
|
|
58
|
+
<kp-icon name="search" class="kp-search-bar__leading" />
|
|
59
|
+
<input
|
|
60
|
+
class="kp-search-bar__input"
|
|
61
|
+
type="search"
|
|
62
|
+
[placeholder]="placeholder"
|
|
63
|
+
[value]="value"
|
|
64
|
+
(input)="handleInput($event)"
|
|
65
|
+
(focus)="focused = true"
|
|
66
|
+
(blur)="focused = false"
|
|
67
|
+
[attr.aria-label]="placeholder"
|
|
68
|
+
/>
|
|
69
|
+
@if (value) {
|
|
70
|
+
<kp-button
|
|
71
|
+
size="xs"
|
|
72
|
+
variant="ghost"
|
|
73
|
+
color="neutral"
|
|
74
|
+
[iconOnly]="true"
|
|
75
|
+
aria-label="Clear"
|
|
76
|
+
(click)="clear()"
|
|
77
|
+
>
|
|
78
|
+
<svg kpButtonIconLeft viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
79
|
+
</kp-button>
|
|
80
|
+
} @else if (showShortcutHint) {
|
|
81
|
+
<kbd class="kp-search-bar__shortcut">{{ shortcutHint }}</kbd>
|
|
82
|
+
}
|
|
83
|
+
</div>
|
|
84
|
+
} @else {
|
|
85
|
+
<div class="kp-search-bar__palette">
|
|
86
|
+
<div class="kp-search-bar__palette-header">
|
|
87
|
+
<kp-icon name="search" class="kp-search-bar__leading" />
|
|
88
|
+
<input
|
|
89
|
+
class="kp-search-bar__palette-input"
|
|
90
|
+
type="search"
|
|
91
|
+
[placeholder]="placeholder"
|
|
92
|
+
[value]="value"
|
|
93
|
+
(input)="handleInput($event)"
|
|
94
|
+
autofocus
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div class="kp-search-bar__groups" role="listbox">
|
|
99
|
+
@for (group of groups; track group.label) {
|
|
100
|
+
<div class="kp-search-bar__group">
|
|
101
|
+
<div class="kp-search-bar__group-label">{{ group.label }}</div>
|
|
102
|
+
@for (item of group.items; track item.id) {
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
role="option"
|
|
106
|
+
class="kp-search-bar__item"
|
|
107
|
+
(click)="itemClick.emit(item)"
|
|
108
|
+
>
|
|
109
|
+
@if (item.icon) {
|
|
110
|
+
<kp-icon [name]="item.icon" class="kp-search-bar__item-icon" />
|
|
111
|
+
}
|
|
112
|
+
<span class="kp-search-bar__item-label">{{ item.label }}</span>
|
|
113
|
+
@if (item.shortcut) {
|
|
114
|
+
<kbd class="kp-search-bar__shortcut">{{ item.shortcut }}</kbd>
|
|
115
|
+
}
|
|
116
|
+
</button>
|
|
117
|
+
}
|
|
118
|
+
</div>
|
|
119
|
+
}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="kp-search-bar__footer">
|
|
123
|
+
<span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>
|
|
124
|
+
<span><kbd>↵</kbd> select</span>
|
|
125
|
+
<span><kbd>esc</kbd> close</span>
|
|
126
|
+
<span class="kp-search-bar__footer-brand">Powered by Kanso</span>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
}
|
|
130
|
+
`, isInline: true, styles: [":host{display:inline-block;font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}:host(.kp-search-bar--command-palette){display:block;width:640px;max-width:100%}.kp-search-bar__wrap{display:inline-flex;align-items:center;gap:8px;width:var(--kp-search-w, 400px);height:var(--kp-search-h, 36px);padding:0 10px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:8px;background:var(--kp-color-gray-50, var(--kp-color-gray-50));transition:background var(--kp-motion-duration-fast) ease,border-color .12s ease,box-shadow .12s ease}.kp-search-bar__wrap--focused,.kp-search-bar__wrap:focus-within{background:var(--kp-color-white, var(--kp-color-white));border-color:var(--kp-color-blue-500, var(--kp-color-blue-500));box-shadow:0 0 0 3px var(--kp-color-overlay-focus-ring)}:host(.kp-search-bar--sm){--kp-search-w: 320px;--kp-search-h: 32px;--kp-search-fs: 13px}:host(.kp-search-bar--md){--kp-search-w: 400px;--kp-search-h: 36px;--kp-search-fs: 14px}:host(.kp-search-bar--lg){--kp-search-w: 480px;--kp-search-h: 44px;--kp-search-fs: 15px}.kp-search-bar__leading{flex:0 0 auto;font-size:18px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__input{all:unset;flex:1 1 auto;min-width:0;height:100%;font-size:var(--kp-search-fs, 14px);color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__input::placeholder{color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__input::-webkit-search-cancel-button{display:none}.kp-search-bar__shortcut{display:inline-flex;align-items:center;padding:2px 6px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:4px;background:var(--kp-color-white, var(--kp-color-white));color:var(--kp-color-gray-600, var(--kp-color-gray-600));font-family:var(--kp-font-family-mono, \"JetBrains Mono\", ui-monospace, monospace);font-size:11px;font-weight:500}.kp-search-bar__palette{display:flex;flex-direction:column;border-radius:12px;border:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));background:var(--kp-color-search-palette-bg, var(--kp-color-white));box-shadow:var(--kp-elevation-floating);overflow:hidden}.kp-search-bar__palette-header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200))}.kp-search-bar__palette-header .kp-search-bar__leading{font-size:20px}.kp-search-bar__palette-input{all:unset;flex:1 1 auto;font-size:16px;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__palette-input::placeholder{color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__groups{padding:8px;max-height:420px;overflow-y:auto}.kp-search-bar__group{display:flex;flex-direction:column}.kp-search-bar__group+.kp-search-bar__group{margin-top:8px}.kp-search-bar__group-label{padding:8px 12px 4px;font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--kp-color-search-palette-group-label, var(--kp-color-gray-500))}.kp-search-bar__item{all:unset;display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:6px;font-size:13px;color:var(--kp-color-gray-700, var(--kp-color-gray-700));cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-search-bar__item:hover{background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__item-icon{font-size:16px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__item:hover .kp-search-bar__item-icon{color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__item-label{flex:1 1 auto}.kp-search-bar__footer{display:flex;align-items:center;gap:16px;padding:8px 14px;border-top:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));background:var(--kp-color-gray-50, var(--kp-color-gray-50));font-size:11px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__footer kbd{display:inline-flex;align-items:center;padding:1px 5px;margin-inline-end:2px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:3px;background:var(--kp-color-white, var(--kp-color-white));color:var(--kp-color-gray-600, var(--kp-color-gray-600));font-family:inherit;font-size:10px}.kp-search-bar__footer-brand{margin-inline-start:auto;color:var(--kp-color-gray-400, var(--kp-color-gray-400))}\n"], dependencies: [{ kind: "component", type: KpButtonComponent, selector: "kp-button", inputs: ["size", "variant", "color", "disabled", "loading", "iconOnly", "forceState"] }, { kind: "component", type: KpIconComponent, selector: "kp-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
131
|
+
}
|
|
132
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpSearchBarComponent, decorators: [{
|
|
133
|
+
type: Component,
|
|
134
|
+
args: [{ selector: 'kp-search-bar', imports: [KpButtonComponent, KpIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses' }, template: `
|
|
135
|
+
@if (variant === 'inline') {
|
|
136
|
+
<div class="kp-search-bar__wrap" [class.kp-search-bar__wrap--focused]="focused">
|
|
137
|
+
<kp-icon name="search" class="kp-search-bar__leading" />
|
|
138
|
+
<input
|
|
139
|
+
class="kp-search-bar__input"
|
|
140
|
+
type="search"
|
|
141
|
+
[placeholder]="placeholder"
|
|
142
|
+
[value]="value"
|
|
143
|
+
(input)="handleInput($event)"
|
|
144
|
+
(focus)="focused = true"
|
|
145
|
+
(blur)="focused = false"
|
|
146
|
+
[attr.aria-label]="placeholder"
|
|
147
|
+
/>
|
|
148
|
+
@if (value) {
|
|
149
|
+
<kp-button
|
|
150
|
+
size="xs"
|
|
151
|
+
variant="ghost"
|
|
152
|
+
color="neutral"
|
|
153
|
+
[iconOnly]="true"
|
|
154
|
+
aria-label="Clear"
|
|
155
|
+
(click)="clear()"
|
|
156
|
+
>
|
|
157
|
+
<svg kpButtonIconLeft viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
158
|
+
</kp-button>
|
|
159
|
+
} @else if (showShortcutHint) {
|
|
160
|
+
<kbd class="kp-search-bar__shortcut">{{ shortcutHint }}</kbd>
|
|
161
|
+
}
|
|
162
|
+
</div>
|
|
163
|
+
} @else {
|
|
164
|
+
<div class="kp-search-bar__palette">
|
|
165
|
+
<div class="kp-search-bar__palette-header">
|
|
166
|
+
<kp-icon name="search" class="kp-search-bar__leading" />
|
|
167
|
+
<input
|
|
168
|
+
class="kp-search-bar__palette-input"
|
|
169
|
+
type="search"
|
|
170
|
+
[placeholder]="placeholder"
|
|
171
|
+
[value]="value"
|
|
172
|
+
(input)="handleInput($event)"
|
|
173
|
+
autofocus
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div class="kp-search-bar__groups" role="listbox">
|
|
178
|
+
@for (group of groups; track group.label) {
|
|
179
|
+
<div class="kp-search-bar__group">
|
|
180
|
+
<div class="kp-search-bar__group-label">{{ group.label }}</div>
|
|
181
|
+
@for (item of group.items; track item.id) {
|
|
182
|
+
<button
|
|
183
|
+
type="button"
|
|
184
|
+
role="option"
|
|
185
|
+
class="kp-search-bar__item"
|
|
186
|
+
(click)="itemClick.emit(item)"
|
|
187
|
+
>
|
|
188
|
+
@if (item.icon) {
|
|
189
|
+
<kp-icon [name]="item.icon" class="kp-search-bar__item-icon" />
|
|
190
|
+
}
|
|
191
|
+
<span class="kp-search-bar__item-label">{{ item.label }}</span>
|
|
192
|
+
@if (item.shortcut) {
|
|
193
|
+
<kbd class="kp-search-bar__shortcut">{{ item.shortcut }}</kbd>
|
|
194
|
+
}
|
|
195
|
+
</button>
|
|
196
|
+
}
|
|
197
|
+
</div>
|
|
198
|
+
}
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="kp-search-bar__footer">
|
|
202
|
+
<span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>
|
|
203
|
+
<span><kbd>↵</kbd> select</span>
|
|
204
|
+
<span><kbd>esc</kbd> close</span>
|
|
205
|
+
<span class="kp-search-bar__footer-brand">Powered by Kanso</span>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
}
|
|
209
|
+
`, styles: [":host{display:inline-block;font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}:host(.kp-search-bar--command-palette){display:block;width:640px;max-width:100%}.kp-search-bar__wrap{display:inline-flex;align-items:center;gap:8px;width:var(--kp-search-w, 400px);height:var(--kp-search-h, 36px);padding:0 10px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:8px;background:var(--kp-color-gray-50, var(--kp-color-gray-50));transition:background var(--kp-motion-duration-fast) ease,border-color .12s ease,box-shadow .12s ease}.kp-search-bar__wrap--focused,.kp-search-bar__wrap:focus-within{background:var(--kp-color-white, var(--kp-color-white));border-color:var(--kp-color-blue-500, var(--kp-color-blue-500));box-shadow:0 0 0 3px var(--kp-color-overlay-focus-ring)}:host(.kp-search-bar--sm){--kp-search-w: 320px;--kp-search-h: 32px;--kp-search-fs: 13px}:host(.kp-search-bar--md){--kp-search-w: 400px;--kp-search-h: 36px;--kp-search-fs: 14px}:host(.kp-search-bar--lg){--kp-search-w: 480px;--kp-search-h: 44px;--kp-search-fs: 15px}.kp-search-bar__leading{flex:0 0 auto;font-size:18px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__input{all:unset;flex:1 1 auto;min-width:0;height:100%;font-size:var(--kp-search-fs, 14px);color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__input::placeholder{color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__input::-webkit-search-cancel-button{display:none}.kp-search-bar__shortcut{display:inline-flex;align-items:center;padding:2px 6px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:4px;background:var(--kp-color-white, var(--kp-color-white));color:var(--kp-color-gray-600, var(--kp-color-gray-600));font-family:var(--kp-font-family-mono, \"JetBrains Mono\", ui-monospace, monospace);font-size:11px;font-weight:500}.kp-search-bar__palette{display:flex;flex-direction:column;border-radius:12px;border:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));background:var(--kp-color-search-palette-bg, var(--kp-color-white));box-shadow:var(--kp-elevation-floating);overflow:hidden}.kp-search-bar__palette-header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200))}.kp-search-bar__palette-header .kp-search-bar__leading{font-size:20px}.kp-search-bar__palette-input{all:unset;flex:1 1 auto;font-size:16px;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__palette-input::placeholder{color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__groups{padding:8px;max-height:420px;overflow-y:auto}.kp-search-bar__group{display:flex;flex-direction:column}.kp-search-bar__group+.kp-search-bar__group{margin-top:8px}.kp-search-bar__group-label{padding:8px 12px 4px;font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--kp-color-search-palette-group-label, var(--kp-color-gray-500))}.kp-search-bar__item{all:unset;display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:6px;font-size:13px;color:var(--kp-color-gray-700, var(--kp-color-gray-700));cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-search-bar__item:hover{background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__item-icon{font-size:16px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__item:hover .kp-search-bar__item-icon{color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-search-bar__item-label{flex:1 1 auto}.kp-search-bar__footer{display:flex;align-items:center;gap:16px;padding:8px 14px;border-top:1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));background:var(--kp-color-gray-50, var(--kp-color-gray-50));font-size:11px;color:var(--kp-color-gray-500, var(--kp-color-gray-500))}.kp-search-bar__footer kbd{display:inline-flex;align-items:center;padding:1px 5px;margin-inline-end:2px;border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));border-radius:3px;background:var(--kp-color-white, var(--kp-color-white));color:var(--kp-color-gray-600, var(--kp-color-gray-600));font-family:inherit;font-size:10px}.kp-search-bar__footer-brand{margin-inline-start:auto;color:var(--kp-color-gray-400, var(--kp-color-gray-400))}\n"] }]
|
|
210
|
+
}], propDecorators: { variant: [{
|
|
211
|
+
type: Input
|
|
212
|
+
}], size: [{
|
|
213
|
+
type: Input
|
|
214
|
+
}], placeholder: [{
|
|
215
|
+
type: Input
|
|
216
|
+
}], value: [{
|
|
217
|
+
type: Input
|
|
218
|
+
}], showShortcutHint: [{
|
|
219
|
+
type: Input
|
|
220
|
+
}], shortcutHint: [{
|
|
221
|
+
type: Input
|
|
222
|
+
}], groups: [{
|
|
223
|
+
type: Input
|
|
224
|
+
}], valueChange: [{
|
|
225
|
+
type: Output
|
|
226
|
+
}], search: [{
|
|
227
|
+
type: Output
|
|
228
|
+
}], itemClick: [{
|
|
229
|
+
type: Output
|
|
230
|
+
}] } });
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Generated bundle index. Do not edit.
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
export { KpSearchBarComponent };
|
|
237
|
+
//# sourceMappingURL=kanso-protocol-search-bar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanso-protocol-search-bar.mjs","sources":["../../../../../packages/patterns/search-bar/src/search-bar.component.ts","../../../../../packages/patterns/search-bar/src/kanso-protocol-search-bar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport { KpButtonComponent } from '@kanso-protocol/button';\nimport { KpIconComponent } from '@kanso-protocol/icon';\n\nexport type KpSearchBarVariant = 'inline' | 'command-palette';\nexport type KpSearchBarSize = 'sm' | 'md' | 'lg';\n\nexport interface KpSearchResultItem {\n id: string;\n label: string;\n icon?: string; // Tabler icon name (without 'ti-' prefix)\n shortcut?: string; // e.g. \"⌘1\"\n}\n\nexport interface KpSearchResultGroup {\n label: string; // \"RECENT\", \"ACTIONS\", \"PAGES\"\n items: KpSearchResultItem[];\n}\n\n/**\n * Kanso Protocol — SearchBar\n *\n * Two variants:\n * - `inline` — a simple search input with optional ⌘K shortcut hint,\n * good for header search or page filters.\n * - `command-palette` — a self-contained panel: input + grouped\n * results with shortcuts + footer helper keys. Typically rendered\n * inside a Dialog/Overlay.\n *\n * The component is presentational; filtering results, opening the\n * palette via ⌘K, and committing selection are all consumer\n * responsibilities.\n *\n * @example\n * <!-- Inline in Header -->\n * <kp-search-bar variant=\"inline\" size=\"md\" [showShortcutHint]=\"true\" (search)=\"onSearch($event)\"/>\n *\n * <!-- Command Palette (inside a Dialog) -->\n * <kp-search-bar variant=\"command-palette\" [groups]=\"results\" (itemClick)=\"run($event)\"/>\n */\n@Component({\n selector: 'kp-search-bar',\n imports: [KpButtonComponent, KpIconComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses' },\n template: `\n @if (variant === 'inline') {\n <div class=\"kp-search-bar__wrap\" [class.kp-search-bar__wrap--focused]=\"focused\">\n <kp-icon name=\"search\" class=\"kp-search-bar__leading\" />\n <input\n class=\"kp-search-bar__input\"\n type=\"search\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n (input)=\"handleInput($event)\"\n (focus)=\"focused = true\"\n (blur)=\"focused = false\"\n [attr.aria-label]=\"placeholder\"\n />\n @if (value) {\n <kp-button\n size=\"xs\"\n variant=\"ghost\"\n color=\"neutral\"\n [iconOnly]=\"true\"\n aria-label=\"Clear\"\n (click)=\"clear()\"\n >\n <svg kpButtonIconLeft viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </kp-button>\n } @else if (showShortcutHint) {\n <kbd class=\"kp-search-bar__shortcut\">{{ shortcutHint }}</kbd>\n }\n </div>\n } @else {\n <div class=\"kp-search-bar__palette\">\n <div class=\"kp-search-bar__palette-header\">\n <kp-icon name=\"search\" class=\"kp-search-bar__leading\" />\n <input\n class=\"kp-search-bar__palette-input\"\n type=\"search\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n (input)=\"handleInput($event)\"\n autofocus\n />\n </div>\n\n <div class=\"kp-search-bar__groups\" role=\"listbox\">\n @for (group of groups; track group.label) {\n <div class=\"kp-search-bar__group\">\n <div class=\"kp-search-bar__group-label\">{{ group.label }}</div>\n @for (item of group.items; track item.id) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"kp-search-bar__item\"\n (click)=\"itemClick.emit(item)\"\n >\n @if (item.icon) {\n <kp-icon [name]=\"item.icon\" class=\"kp-search-bar__item-icon\" />\n }\n <span class=\"kp-search-bar__item-label\">{{ item.label }}</span>\n @if (item.shortcut) {\n <kbd class=\"kp-search-bar__shortcut\">{{ item.shortcut }}</kbd>\n }\n </button>\n }\n </div>\n }\n </div>\n\n <div class=\"kp-search-bar__footer\">\n <span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>\n <span><kbd>↵</kbd> select</span>\n <span><kbd>esc</kbd> close</span>\n <span class=\"kp-search-bar__footer-brand\">Powered by Kanso</span>\n </div>\n </div>\n }\n `,\n styles: [`\n :host {\n display: inline-block;\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n }\n :host(.kp-search-bar--command-palette) { display: block; width: 640px; max-width: 100%; }\n\n /* --- inline variant --- */\n .kp-search-bar__wrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n width: var(--kp-search-w, 400px);\n height: var(--kp-search-h, 36px);\n padding: 0 10px;\n border: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n border-radius: 8px;\n background: var(--kp-color-gray-50, var(--kp-color-gray-50));\n transition: background var(--kp-motion-duration-fast) ease, border-color 120ms ease, box-shadow 120ms ease;\n }\n .kp-search-bar__wrap--focused,\n .kp-search-bar__wrap:focus-within {\n background: var(--kp-color-white, var(--kp-color-white));\n border-color: var(--kp-color-blue-500, var(--kp-color-blue-500));\n box-shadow: 0 0 0 3px var(--kp-color-overlay-focus-ring);\n }\n\n :host(.kp-search-bar--sm) { --kp-search-w: 320px; --kp-search-h: 32px; --kp-search-fs: 13px; }\n :host(.kp-search-bar--md) { --kp-search-w: 400px; --kp-search-h: 36px; --kp-search-fs: 14px; }\n :host(.kp-search-bar--lg) { --kp-search-w: 480px; --kp-search-h: 44px; --kp-search-fs: 15px; }\n\n .kp-search-bar__leading {\n flex: 0 0 auto;\n font-size: 18px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n }\n\n .kp-search-bar__input {\n all: unset;\n flex: 1 1 auto;\n min-width: 0;\n height: 100%;\n font-size: var(--kp-search-fs, 14px);\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-search-bar__input::placeholder { color: var(--kp-color-gray-500, var(--kp-color-gray-500)); }\n .kp-search-bar__input::-webkit-search-cancel-button { display: none; }\n\n\n .kp-search-bar__shortcut {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n border: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n border-radius: 4px;\n background: var(--kp-color-white, var(--kp-color-white));\n color: var(--kp-color-gray-600, var(--kp-color-gray-600));\n font-family: var(--kp-font-family-mono, 'JetBrains Mono', ui-monospace, monospace);\n font-size: 11px;\n font-weight: 500;\n }\n\n /* --- command palette variant --- */\n .kp-search-bar__palette {\n display: flex;\n flex-direction: column;\n border-radius: 12px;\n border: 1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));\n background: var(--kp-color-search-palette-bg, var(--kp-color-white));\n box-shadow: var(--kp-elevation-floating);\n overflow: hidden;\n }\n\n .kp-search-bar__palette-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 16px;\n border-bottom: 1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));\n }\n .kp-search-bar__palette-header .kp-search-bar__leading { font-size: 20px; }\n .kp-search-bar__palette-input {\n all: unset;\n flex: 1 1 auto;\n font-size: 16px;\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-search-bar__palette-input::placeholder { color: var(--kp-color-gray-500, var(--kp-color-gray-500)); }\n\n .kp-search-bar__groups {\n padding: 8px;\n max-height: 420px;\n overflow-y: auto;\n }\n .kp-search-bar__group { display: flex; flex-direction: column; }\n .kp-search-bar__group + .kp-search-bar__group { margin-top: 8px; }\n .kp-search-bar__group-label {\n padding: 8px 12px 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n color: var(--kp-color-search-palette-group-label, var(--kp-color-gray-500));\n }\n\n .kp-search-bar__item {\n all: unset;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: 13px;\n color: var(--kp-color-gray-700, var(--kp-color-gray-700));\n cursor: pointer;\n transition: background var(--kp-motion-duration-fast) ease, color 120ms ease;\n }\n .kp-search-bar__item:hover { background: var(--kp-color-gray-100, var(--kp-color-gray-100)); color: var(--kp-color-gray-900, var(--kp-color-gray-900)); }\n .kp-search-bar__item-icon { font-size: 16px; color: var(--kp-color-gray-500, var(--kp-color-gray-500)); }\n .kp-search-bar__item:hover .kp-search-bar__item-icon { color: var(--kp-color-gray-900, var(--kp-color-gray-900)); }\n .kp-search-bar__item-label { flex: 1 1 auto; }\n\n .kp-search-bar__footer {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 8px 14px;\n border-top: 1px solid var(--kp-color-search-palette-border, var(--kp-color-gray-200));\n background: var(--kp-color-gray-50, var(--kp-color-gray-50));\n font-size: 11px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n }\n .kp-search-bar__footer kbd {\n display: inline-flex;\n align-items: center;\n padding: 1px 5px;\n margin-inline-end: 2px;\n border: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n border-radius: 3px;\n background: var(--kp-color-white, var(--kp-color-white));\n color: var(--kp-color-gray-600, var(--kp-color-gray-600));\n font-family: inherit;\n font-size: 10px;\n }\n .kp-search-bar__footer-brand { margin-inline-start: auto; color: var(--kp-color-gray-400, var(--kp-color-gray-400)); }\n `],\n})\nexport class KpSearchBarComponent {\n @Input() variant: KpSearchBarVariant = 'inline';\n @Input() size: KpSearchBarSize = 'md';\n @Input() placeholder = 'Search anything...';\n @Input() value = '';\n @Input() showShortcutHint = true;\n @Input() shortcutHint = '⌘K';\n /** Result groups — only rendered in `command-palette` variant */\n @Input() groups: KpSearchResultGroup[] = [];\n\n @Output() valueChange = new EventEmitter<string>();\n @Output() search = new EventEmitter<string>();\n @Output() itemClick = new EventEmitter<KpSearchResultItem>();\n\n focused = false;\n\n get hostClasses(): string {\n return `kp-search-bar kp-search-bar--${this.variant} kp-search-bar--${this.size}`;\n }\n\n handleInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n this.value = target.value;\n this.valueChange.emit(this.value);\n this.search.emit(this.value);\n }\n\n clear(): void {\n this.value = '';\n this.valueChange.emit('');\n this.search.emit('');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAyBA;;;;;;;;;;;;;;;;;;;;AAoBG;MAqOU,oBAAoB,CAAA;IACtB,OAAO,GAAuB,QAAQ;IACtC,IAAI,GAAoB,IAAI;IAC5B,WAAW,GAAG,oBAAoB;IAClC,KAAK,GAAG,EAAE;IACV,gBAAgB,GAAG,IAAI;IACvB,YAAY,GAAG,IAAI;;IAEnB,MAAM,GAA0B,EAAE;AAEjC,IAAA,WAAW,GAAG,IAAI,YAAY,EAAU;AACxC,IAAA,MAAM,GAAG,IAAI,YAAY,EAAU;AACnC,IAAA,SAAS,GAAG,IAAI,YAAY,EAAsB;IAE5D,OAAO,GAAG,KAAK;AAEf,IAAA,IAAI,WAAW,GAAA;QACb,OAAO,CAAA,6BAAA,EAAgC,IAAI,CAAC,OAAO,mBAAmB,IAAI,CAAC,IAAI,CAAA,CAAE;IACnF;AAEA,IAAA,WAAW,CAAC,KAAY,EAAA;AACtB,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;AAC/C,QAAA,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK;QACzB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IAC9B;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,KAAK,GAAG,EAAE;AACf,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;AACzB,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IACtB;uGA/BW,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,MAAA,EAAA,WAAA,EAAA,aAAA,EAAA,KAAA,EAAA,OAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,YAAA,EAAA,cAAA,EAAA,MAAA,EAAA,QAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,MAAA,EAAA,QAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,aAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/NrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2ET,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,02IAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA9ES,iBAAiB,6IAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAkOjC,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBApOhC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,WAChB,CAAC,iBAAiB,EAAE,eAAe,CAAC,EAAA,eAAA,EAC5B,uBAAuB,CAAC,MAAM,QACzC,EAAE,SAAS,EAAE,aAAa,EAAE,EAAA,QAAA,EACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2ET,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,02IAAA,CAAA,EAAA;;sBAqJA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBAEA;;sBACA;;sBACA;;;AC9RH;;AAEG;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kanso-protocol/search-bar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/core": "^18.0.0",
|
|
7
|
+
"@angular/common": "^18.0.0",
|
|
8
|
+
"@kanso-protocol/core": "^0.0.1",
|
|
9
|
+
"@kanso-protocol/button": ">=0.1.0"
|
|
10
|
+
},
|
|
11
|
+
"description": "Kanso Protocol — search-bar (pattern).",
|
|
12
|
+
"author": "GregNBlack",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/GregNBlack/kanso-protocol.git",
|
|
16
|
+
"directory": "packages/patterns/search-bar"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://gregnblack.github.io/kanso-protocol/?path=/docs/patterns-searchbar--docs",
|
|
19
|
+
"bugs": "https://github.com/GregNBlack/kanso-protocol/issues",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"design-system",
|
|
22
|
+
"angular",
|
|
23
|
+
"kanso",
|
|
24
|
+
"search-bar"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"module": "fesm2022/kanso-protocol-search-bar.mjs",
|
|
28
|
+
"typings": "types/kanso-protocol-search-bar.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": {
|
|
31
|
+
"default": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./types/kanso-protocol-search-bar.d.ts",
|
|
35
|
+
"default": "./fesm2022/kanso-protocol-search-bar.mjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"type": "module",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"tslib": "^2.3.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
type KpSearchBarVariant = 'inline' | 'command-palette';
|
|
5
|
+
type KpSearchBarSize = 'sm' | 'md' | 'lg';
|
|
6
|
+
interface KpSearchResultItem {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
shortcut?: string;
|
|
11
|
+
}
|
|
12
|
+
interface KpSearchResultGroup {
|
|
13
|
+
label: string;
|
|
14
|
+
items: KpSearchResultItem[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Kanso Protocol — SearchBar
|
|
18
|
+
*
|
|
19
|
+
* Two variants:
|
|
20
|
+
* - `inline` — a simple search input with optional ⌘K shortcut hint,
|
|
21
|
+
* good for header search or page filters.
|
|
22
|
+
* - `command-palette` — a self-contained panel: input + grouped
|
|
23
|
+
* results with shortcuts + footer helper keys. Typically rendered
|
|
24
|
+
* inside a Dialog/Overlay.
|
|
25
|
+
*
|
|
26
|
+
* The component is presentational; filtering results, opening the
|
|
27
|
+
* palette via ⌘K, and committing selection are all consumer
|
|
28
|
+
* responsibilities.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <!-- Inline in Header -->
|
|
32
|
+
* <kp-search-bar variant="inline" size="md" [showShortcutHint]="true" (search)="onSearch($event)"/>
|
|
33
|
+
*
|
|
34
|
+
* <!-- Command Palette (inside a Dialog) -->
|
|
35
|
+
* <kp-search-bar variant="command-palette" [groups]="results" (itemClick)="run($event)"/>
|
|
36
|
+
*/
|
|
37
|
+
declare class KpSearchBarComponent {
|
|
38
|
+
variant: KpSearchBarVariant;
|
|
39
|
+
size: KpSearchBarSize;
|
|
40
|
+
placeholder: string;
|
|
41
|
+
value: string;
|
|
42
|
+
showShortcutHint: boolean;
|
|
43
|
+
shortcutHint: string;
|
|
44
|
+
/** Result groups — only rendered in `command-palette` variant */
|
|
45
|
+
groups: KpSearchResultGroup[];
|
|
46
|
+
valueChange: EventEmitter<string>;
|
|
47
|
+
search: EventEmitter<string>;
|
|
48
|
+
itemClick: EventEmitter<KpSearchResultItem>;
|
|
49
|
+
focused: boolean;
|
|
50
|
+
get hostClasses(): string;
|
|
51
|
+
handleInput(event: Event): void;
|
|
52
|
+
clear(): void;
|
|
53
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<KpSearchBarComponent, never>;
|
|
54
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<KpSearchBarComponent, "kp-search-bar", never, { "variant": { "alias": "variant"; "required": false; }; "size": { "alias": "size"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "value": { "alias": "value"; "required": false; }; "showShortcutHint": { "alias": "showShortcutHint"; "required": false; }; "shortcutHint": { "alias": "shortcutHint"; "required": false; }; "groups": { "alias": "groups"; "required": false; }; }, { "valueChange": "valueChange"; "search": "search"; "itemClick": "itemClick"; }, never, never, true, never>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { KpSearchBarComponent };
|
|
58
|
+
export type { KpSearchBarSize, KpSearchBarVariant, KpSearchResultGroup, KpSearchResultItem };
|