@kanso-protocol/search-bar 0.1.1 → 0.2.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.
@@ -2,6 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { KpButtonComponent } from '@kanso-protocol/button';
4
4
  import { KpIconComponent } from '@kanso-protocol/icon';
5
+ import { injectKpStrings } from '@kanso-protocol/i18n';
5
6
 
6
7
  /**
7
8
  * Kanso Protocol — SearchBar
@@ -27,16 +28,23 @@ import { KpIconComponent } from '@kanso-protocol/icon';
27
28
  class KpSearchBarComponent {
28
29
  variant = 'inline';
29
30
  size = 'md';
30
- placeholder = 'Search anything...';
31
+ placeholder = null;
31
32
  value = '';
32
33
  showShortcutHint = true;
33
- shortcutHint = '⌘K';
34
+ shortcutHint = null;
34
35
  /** Result groups — only rendered in `command-palette` variant */
35
36
  groups = [];
36
37
  valueChange = new EventEmitter();
37
38
  search = new EventEmitter();
38
39
  itemClick = new EventEmitter();
39
40
  focused = false;
41
+ strings = injectKpStrings();
42
+ get resolvedPlaceholder() {
43
+ return this.placeholder ?? this.strings.searchPlaceholder;
44
+ }
45
+ get resolvedShortcutHint() {
46
+ return this.shortcutHint ?? this.strings.commandPaletteHint;
47
+ }
40
48
  get hostClasses() {
41
49
  return `kp-search-bar kp-search-bar--${this.variant} kp-search-bar--${this.size}`;
42
50
  }
@@ -59,12 +67,12 @@ class KpSearchBarComponent {
59
67
  <input
60
68
  class="kp-search-bar__input"
61
69
  type="search"
62
- [placeholder]="placeholder"
70
+ [placeholder]="resolvedPlaceholder"
63
71
  [value]="value"
64
72
  (input)="handleInput($event)"
65
73
  (focus)="focused = true"
66
74
  (blur)="focused = false"
67
- [attr.aria-label]="placeholder"
75
+ [attr.aria-label]="resolvedPlaceholder"
68
76
  />
69
77
  @if (value) {
70
78
  <kp-button
@@ -78,7 +86,7 @@ class KpSearchBarComponent {
78
86
  <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
87
  </kp-button>
80
88
  } @else if (showShortcutHint) {
81
- <kbd class="kp-search-bar__shortcut">{{ shortcutHint }}</kbd>
89
+ <kbd class="kp-search-bar__shortcut">{{ resolvedShortcutHint }}</kbd>
82
90
  }
83
91
  </div>
84
92
  } @else {
@@ -88,8 +96,8 @@ class KpSearchBarComponent {
88
96
  <input
89
97
  class="kp-search-bar__palette-input"
90
98
  type="search"
91
- [placeholder]="placeholder"
92
- [attr.aria-label]="placeholder"
99
+ [placeholder]="resolvedPlaceholder"
100
+ [attr.aria-label]="resolvedPlaceholder"
93
101
  [value]="value"
94
102
  (input)="handleInput($event)"
95
103
  autofocus
@@ -139,12 +147,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
139
147
  <input
140
148
  class="kp-search-bar__input"
141
149
  type="search"
142
- [placeholder]="placeholder"
150
+ [placeholder]="resolvedPlaceholder"
143
151
  [value]="value"
144
152
  (input)="handleInput($event)"
145
153
  (focus)="focused = true"
146
154
  (blur)="focused = false"
147
- [attr.aria-label]="placeholder"
155
+ [attr.aria-label]="resolvedPlaceholder"
148
156
  />
149
157
  @if (value) {
150
158
  <kp-button
@@ -158,7 +166,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
158
166
  <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>
159
167
  </kp-button>
160
168
  } @else if (showShortcutHint) {
161
- <kbd class="kp-search-bar__shortcut">{{ shortcutHint }}</kbd>
169
+ <kbd class="kp-search-bar__shortcut">{{ resolvedShortcutHint }}</kbd>
162
170
  }
163
171
  </div>
164
172
  } @else {
@@ -168,8 +176,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
168
176
  <input
169
177
  class="kp-search-bar__palette-input"
170
178
  type="search"
171
- [placeholder]="placeholder"
172
- [attr.aria-label]="placeholder"
179
+ [placeholder]="resolvedPlaceholder"
180
+ [attr.aria-label]="resolvedPlaceholder"
173
181
  [value]="value"
174
182
  (input)="handleInput($event)"
175
183
  autofocus
@@ -1 +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 [attr.aria-label]=\"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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"}
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';\nimport { injectKpStrings } from '@kanso-protocol/i18n';\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]=\"resolvedPlaceholder\"\n [value]=\"value\"\n (input)=\"handleInput($event)\"\n (focus)=\"focused = true\"\n (blur)=\"focused = false\"\n [attr.aria-label]=\"resolvedPlaceholder\"\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\">{{ resolvedShortcutHint }}</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]=\"resolvedPlaceholder\"\n [attr.aria-label]=\"resolvedPlaceholder\"\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: string | null = null;\n @Input() value = '';\n @Input() showShortcutHint = true;\n @Input() shortcutHint: string | null = null;\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 readonly strings = injectKpStrings();\n\n get resolvedPlaceholder(): string {\n return this.placeholder ?? this.strings.searchPlaceholder;\n }\n\n get resolvedShortcutHint(): string {\n return this.shortcutHint ?? this.strings.commandPaletteHint;\n }\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 CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@kanso-protocol/search-bar",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "peerDependencies": {
6
6
  "@angular/core": "^18.0.0",
7
7
  "@angular/common": "^18.0.0",
8
8
  "@kanso-protocol/core": "^0.0.1",
9
- "@kanso-protocol/button": ">=0.1.0"
9
+ "@kanso-protocol/button": ">=0.1.0",
10
+ "@kanso-protocol/i18n": "^0.2.0"
10
11
  },
11
12
  "description": "Kanso Protocol — search-bar (pattern).",
12
13
  "author": "GregNBlack",
@@ -1,3 +1,4 @@
1
+ import * as _kanso_protocol_i18n from '@kanso-protocol/i18n';
1
2
  import * as i0 from '@angular/core';
2
3
  import { EventEmitter } from '@angular/core';
3
4
 
@@ -37,16 +38,19 @@ interface KpSearchResultGroup {
37
38
  declare class KpSearchBarComponent {
38
39
  variant: KpSearchBarVariant;
39
40
  size: KpSearchBarSize;
40
- placeholder: string;
41
+ placeholder: string | null;
41
42
  value: string;
42
43
  showShortcutHint: boolean;
43
- shortcutHint: string;
44
+ shortcutHint: string | null;
44
45
  /** Result groups — only rendered in `command-palette` variant */
45
46
  groups: KpSearchResultGroup[];
46
47
  valueChange: EventEmitter<string>;
47
48
  search: EventEmitter<string>;
48
49
  itemClick: EventEmitter<KpSearchResultItem>;
49
50
  focused: boolean;
51
+ readonly strings: _kanso_protocol_i18n.KpLocaleStrings;
52
+ get resolvedPlaceholder(): string;
53
+ get resolvedShortcutHint(): string;
50
54
  get hostClasses(): string;
51
55
  handleInput(event: Event): void;
52
56
  clear(): void;