@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":"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"}
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 };