mertani-web-toolkit 0.1.49 → 0.1.50

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.
@@ -72,6 +72,77 @@
72
72
  border-color: var(--color-text-error-ti);
73
73
  }
74
74
 
75
+ .search-by-wrapper {
76
+ position: relative;
77
+ display: flex;
78
+ align-items: center;
79
+ border-right: 1px solid var(--color-border-form);
80
+ background: var(--color-bg-disabled);
81
+ flex-shrink: 0;
82
+ border-radius: 6px 0 0 6px;
83
+ }
84
+
85
+ .search-by-trigger {
86
+ border: none;
87
+ outline: none;
88
+ background: transparent;
89
+ font-size: 12px;
90
+ font-weight: 600;
91
+ color: var(--color-text-primary);
92
+ cursor: pointer;
93
+ padding: 0 10px;
94
+ height: 100%;
95
+ display: inline-flex;
96
+ align-items: center;
97
+ gap: 8px;
98
+ }
99
+
100
+ .search-by-trigger:disabled {
101
+ cursor: not-allowed;
102
+ opacity: 0.7;
103
+ }
104
+
105
+ .search-by-label {
106
+ white-space: nowrap;
107
+ }
108
+
109
+ .search-by-menu {
110
+ position: absolute;
111
+ top: calc(100% + 4px);
112
+ min-width: 140px;
113
+ background: var(--color-bg-surface);
114
+ border: 1px solid var(--color-border-form);
115
+ border-radius: 4px;
116
+ box-shadow:
117
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
118
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
119
+ overflow: hidden;
120
+ z-index: 99999;
121
+ }
122
+
123
+ .search-by-item {
124
+ width: 100%;
125
+ padding: 10px 12px;
126
+ text-align: left;
127
+ background: transparent;
128
+ border: none;
129
+ font-size: 13px;
130
+ color: var(--color-text-primary);
131
+ cursor: pointer;
132
+ transition:
133
+ background-color 0.15s,
134
+ color 0.15s;
135
+ }
136
+
137
+ .search-by-item + .search-by-item {
138
+ border-top: 1px solid color-mix(in srgb, var(--color-border-form) 45%, transparent);
139
+ }
140
+
141
+ .search-by-item:hover,
142
+ .search-by-item.active {
143
+ background: var(--color-bg-disabled);
144
+ }
145
+
75
146
  .prefix-wrapper {
76
147
  display: flex;
77
148
  align-items: center;
@@ -239,7 +310,7 @@
239
310
 
240
311
  .suggestion-item:hover,
241
312
  .suggestion-item.active {
242
- background: var(--color-bg-surface-hover, #f6f8fa);
313
+ background: var(--color-bg-disabled);
243
314
  }
244
315
 
245
316
  .suggestion-empty {
@@ -12,6 +12,10 @@
12
12
  value?: string;
13
13
  detail?: unknown;
14
14
  };
15
+ type SearchByOption = {
16
+ label: string;
17
+ value: string;
18
+ };
15
19
 
16
20
  interface Props extends HTMLInputAttributes {
17
21
  // ===Styles===
@@ -45,6 +49,8 @@
45
49
 
46
50
  // Suggestions
47
51
  suggestions?: SuggestionOption[];
52
+ searchByOptions?: SearchByOption[];
53
+ searchBy?: string;
48
54
  minChars?: number;
49
55
  maxSuggestions?: number;
50
56
  showSuggestionsOnFocus?: boolean;
@@ -112,6 +118,8 @@
112
118
 
113
119
  // Suggestions
114
120
  suggestions = [],
121
+ searchByOptions = [],
122
+ searchBy = $bindable(''),
115
123
  minChars = 1,
116
124
  maxSuggestions = 8,
117
125
  showSuggestionsOnFocus = true,
@@ -153,11 +161,24 @@
153
161
  let showList = $state(false);
154
162
  let activeIndex = $state(-1);
155
163
  let wrapperEl: HTMLDivElement | null = $state(null);
164
+ let searchByValue = $state(searchBy);
165
+ let searchByOpen = $state(false);
156
166
 
157
167
  $effect(() => {
158
168
  inputValue = value;
159
169
  });
160
170
 
171
+ $effect(() => {
172
+ searchByValue = searchBy;
173
+ });
174
+
175
+ $effect(() => {
176
+ if (!searchByValue && searchByOptions.length) {
177
+ searchByValue = searchByOptions[0].value;
178
+ searchBy = searchByValue;
179
+ }
180
+ });
181
+
161
182
  const sizeConfig = $derived(() => {
162
183
  switch (size) {
163
184
  case 48:
@@ -365,6 +386,23 @@
365
386
  oninput?.(e);
366
387
  }
367
388
 
389
+ function handleSearchByChange(e: Event) {
390
+ const target = e.target as HTMLSelectElement;
391
+ searchByValue = target.value;
392
+ searchBy = target.value;
393
+ }
394
+
395
+ function selectSearchBy(option: SearchByOption) {
396
+ searchByValue = option.value;
397
+ searchBy = option.value;
398
+ searchByOpen = false;
399
+ }
400
+
401
+ function getSearchByLabel() {
402
+ const current = searchByOptions.find((opt) => opt.value === searchByValue);
403
+ return current?.label || 'Search';
404
+ }
405
+
368
406
  function handleChange(e: Event) {
369
407
  const target = e.target as HTMLInputElement;
370
408
  const value = target.value;
@@ -426,6 +464,7 @@
426
464
  function handleClickOutside(e: MouseEvent) {
427
465
  if (wrapperEl && !wrapperEl.contains(e.target as Node)) {
428
466
  closeSuggestions();
467
+ searchByOpen = false;
429
468
  }
430
469
  }
431
470
 
@@ -459,6 +498,41 @@
459
498
  </label>
460
499
  {/if}
461
500
  <div class={wrapperClasses()} style={wrapperStyles()} bind:this={wrapperEl}>
501
+ {#if searchByOptions.length}
502
+ <div class="search-by-wrapper">
503
+ <button
504
+ type="button"
505
+ class="search-by-trigger"
506
+ onclick={() => !disabled && !isLoading && (searchByOpen = !searchByOpen)}
507
+ disabled={disabled || isLoading}
508
+ >
509
+ <span class="search-by-label">{getSearchByLabel()}</span>
510
+ <Icon
511
+ name="bs-chevron-down"
512
+ style="transform-origin: center; transition: transform 0.25s; transform: rotate({searchByOpen
513
+ ? '180deg'
514
+ : '0deg'});"
515
+ />
516
+ </button>
517
+ {#if searchByOpen}
518
+ <div class="search-by-menu">
519
+ {#each searchByOptions as option (option.value)}
520
+ <button
521
+ type="button"
522
+ class="search-by-item"
523
+ class:active={option.value === searchByValue}
524
+ onmousedown={(e) => {
525
+ e.preventDefault();
526
+ selectSearchBy(option);
527
+ }}
528
+ >
529
+ {option.label}
530
+ </button>
531
+ {/each}
532
+ </div>
533
+ {/if}
534
+ </div>
535
+ {/if}
462
536
  {#if prefix}
463
537
  <div class="prefix-wrapper" style={prefixStyles()}>
464
538
  <span class="prefix-text">{prefix}</span>
@@ -7,6 +7,10 @@ type SuggestionOption = string | {
7
7
  value?: string;
8
8
  detail?: unknown;
9
9
  };
10
+ type SearchByOption = {
11
+ label: string;
12
+ value: string;
13
+ };
10
14
  interface Props extends HTMLInputAttributes {
11
15
  labelColor?: string;
12
16
  aligment?: 'side' | 'top';
@@ -30,6 +34,8 @@ interface Props extends HTMLInputAttributes {
30
34
  prefix?: string;
31
35
  suffix?: string;
32
36
  suggestions?: SuggestionOption[];
37
+ searchByOptions?: SearchByOption[];
38
+ searchBy?: string;
33
39
  minChars?: number;
34
40
  maxSuggestions?: number;
35
41
  showSuggestionsOnFocus?: boolean;
@@ -56,6 +62,6 @@ interface Props extends HTMLInputAttributes {
56
62
  class?: string;
57
63
  style?: string;
58
64
  }
59
- declare const TextInputSuggestion: import("svelte").Component<Props, {}, "value">;
65
+ declare const TextInputSuggestion: import("svelte").Component<Props, {}, "value" | "searchBy">;
60
66
  type TextInputSuggestion = ReturnType<typeof TextInputSuggestion>;
61
67
  export default TextInputSuggestion;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mertani-web-toolkit",
3
- "version": "0.1.49",
3
+ "version": "0.1.50",
4
4
  "homepage": "https://storybook.mertani.com/",
5
5
  "scripts": {
6
6
  "dev": "vite dev",