@opendesign-plus-test/components 0.0.1-rc.50 → 0.0.1-rc.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/chunk-OElCookieNotice.cjs.js +1 -1
  2. package/dist/chunk-OElCookieNotice.es.js +68 -48
  3. package/dist/components/OHeaderSearch.vue.d.ts +814 -534
  4. package/dist/components/OThemeSwitcher.vue.d.ts +5 -2
  5. package/dist/components/activity/index.d.ts +2 -2
  6. package/dist/components/meeting/index.d.ts +3 -3
  7. package/dist/components/search/OSearchInput.vue.d.ts +1003 -0
  8. package/dist/components/search/composables/useImageSearch.d.ts +48 -0
  9. package/dist/components/search/composables/useKeywordHighlight.d.ts +2 -0
  10. package/dist/components/search/composables/useSearchHistory.d.ts +14 -0
  11. package/dist/components/search/index.d.ts +590 -0
  12. package/dist/components/search/internal/HighlightText.vue.d.ts +9 -0
  13. package/dist/components/search/internal/SearchImageInput.vue.d.ts +716 -0
  14. package/dist/components/search/internal/SearchPanel.vue.d.ts +100 -0
  15. package/dist/components/search/types.d.ts +20 -0
  16. package/dist/components.cjs.js +42 -42
  17. package/dist/components.css +1 -1
  18. package/dist/components.es.js +11517 -10501
  19. package/dist/index.d.ts +1 -0
  20. package/package.json +4 -2
  21. package/src/assets/svg-icons/icon-delete-hover.svg +4 -0
  22. package/src/assets/svg-icons/icon-delete.svg +5 -1
  23. package/src/assets/svg-icons/icon-image-close.svg +4 -0
  24. package/src/assets/svg-icons/icon-image-upload.svg +3 -0
  25. package/src/assets/svg-icons/icon-image-zoomin.svg +3 -0
  26. package/src/assets/svg-icons/icon-refresh.svg +3 -0
  27. package/src/components/OHeaderSearch.vue +437 -415
  28. package/src/components/OThemeSwitcher.vue +27 -51
  29. package/src/components/activity/OActivityApproval.vue +6 -10
  30. package/src/components/activity/OActivityForm.vue +3 -5
  31. package/src/components/activity/{OActivityMyCalendar.vue → OMyActivityCalendar.vue} +21 -43
  32. package/src/components/activity/index.ts +4 -4
  33. package/src/components/events/OEventsApply.vue +1 -2
  34. package/src/components/events/OEventsCalendar.vue +5 -7
  35. package/src/components/events/OEventsList.vue +4 -6
  36. package/src/components/meeting/OMeetingCalendar.vue +12 -15
  37. package/src/components/meeting/OMeetingForm.vue +10 -16
  38. package/src/components/meeting/OMeetingPlayback.vue +8 -32
  39. package/src/components/meeting/{OMeetingMyCalendar.vue → OMyMeetingCalendar.vue} +38 -73
  40. package/src/components/meeting/{OMeetingSigCalendar.vue → OSigMeetingCalendar.vue} +3 -6
  41. package/src/components/meeting/components/OMeetingCalendarList.vue +3 -3
  42. package/src/components/meeting/components/OMeetingCalendarSelector.vue +1 -1
  43. package/src/components/meeting/components/OMeetingPlaybackSubtitles.vue +9 -10
  44. package/src/components/meeting/components/OMeetingPlaybackVideo.vue +6 -6
  45. package/src/components/meeting/components/{OMeetingSigAside.vue → OSigMeetingAside.vue} +1 -1
  46. package/src/components/meeting/config.ts +1 -1
  47. package/src/components/meeting/index.ts +8 -8
  48. package/src/components/search/OSearchInput.vue +463 -0
  49. package/src/components/search/composables/useImageSearch.ts +157 -0
  50. package/src/components/search/composables/useKeywordHighlight.ts +30 -0
  51. package/src/components/search/composables/useSearchHistory.ts +75 -0
  52. package/src/components/search/index.ts +23 -0
  53. package/src/components/search/internal/HighlightText.vue +37 -0
  54. package/src/components/search/internal/SearchImageInput.vue +488 -0
  55. package/src/components/search/internal/SearchPanel.vue +430 -0
  56. package/src/components/search/types.ts +25 -0
  57. package/src/i18n/en.ts +12 -2
  58. package/src/i18n/zh.ts +10 -0
  59. package/src/index.ts +1 -0
  60. package/vite.config.ts +5 -1
  61. package/src/assets/styles/element-plus.scss +0 -204
  62. /package/dist/components/activity/{OActivityMyCalendar.vue.d.ts → OMyActivityCalendar.vue.d.ts} +0 -0
  63. /package/dist/components/meeting/{OMeetingMyCalendar.vue.d.ts → OMyMeetingCalendar.vue.d.ts} +0 -0
  64. /package/dist/components/meeting/{OMeetingSigCalendar.vue.d.ts → OSigMeetingCalendar.vue.d.ts} +0 -0
  65. /package/dist/components/meeting/components/{OMeetingSigAside.vue.d.ts → OSigMeetingAside.vue.d.ts} +0 -0
@@ -264,7 +264,7 @@ defineExpose({
264
264
  min-height: calc(var(--layout-content-min-height) - 104px);
265
265
  background-color: var(--o-color-fill2);
266
266
  position: relative;
267
- border-radius: var(--meeting-card-radius);
267
+ border-radius: var(--o-radius-xs);
268
268
  overflow: hidden;
269
269
 
270
270
  .video-placeholder {
@@ -443,7 +443,7 @@ defineExpose({
443
443
  width: calc(100% + 5em);
444
444
  max-height: 22em;
445
445
  padding: 8px 16px;
446
- border-radius: var(--meeting-cell-radius);
446
+ border-radius: var(--o-radius-xs);
447
447
  margin-left: -2.5em;
448
448
  background-color: var(--o-color-info2);
449
449
  box-shadow: 0 3px 9px 0 rgba(var(--o-kleinblue-10), 0.08);
@@ -509,7 +509,7 @@ defineExpose({
509
509
  input {
510
510
  order: 1;
511
511
  margin-right: 12px;
512
- border-radius: var(--meeting-input-radius);
512
+ border-radius: 4px;
513
513
  cursor: inherit;
514
514
  position: relative;
515
515
  top: -1px;
@@ -598,7 +598,7 @@ defineExpose({
598
598
  }
599
599
 
600
600
  .vjs-volume-control {
601
- border-radius: var(--meeting-cell-radius);
601
+ border-radius: var(--o-radius-xs);
602
602
  background-color: var(--o-color-info2);
603
603
  box-shadow: 0 3px 9px 0 rgba(var(--o-kleinblue-10), 0.08);
604
604
  backdrop-filter: blur(13.59);
@@ -647,7 +647,7 @@ defineExpose({
647
647
  left: 0;
648
648
 
649
649
  div {
650
- border-radius: var(--meeting-cell-radius);
650
+ border-radius: 4px;
651
651
  }
652
652
 
653
653
  .vjs-progress-holder {
@@ -692,7 +692,7 @@ defineExpose({
692
692
  width: fit-content !important;
693
693
  color: var(--o-color-info1-inverse) !important;
694
694
  background-color: rgba(0, 0, 0, 0.4) !important;
695
- border-radius: var(--meeting-cell-radius);
695
+ border-radius: var(--o-radius-xs);
696
696
  display: block !important;
697
697
  padding: var(--o-gap-1) var(--o-gap-4);
698
698
  @include tip1;
@@ -118,7 +118,7 @@ const changeMonth = (step: number) => {
118
118
 
119
119
  .day-item {
120
120
  height: 64px;
121
- border-radius: var(--meeting-cell-radius);
121
+ border-radius: 4px;
122
122
  background-color: var(--o-color-control2-light);
123
123
  padding: 8px 12px;
124
124
  cursor: pointer;
@@ -7,7 +7,7 @@ import IconSummit from '~icons/meeting/icon-summit.svg';
7
7
  export const TYPE_COLOR_MAP = {
8
8
  summit: 'rgba(var(--o-orange-6))',
9
9
  events: 'rgba(var(--o-cyan-6))',
10
- meeting: 'rgba(var(--o-deepblue-6))',
10
+ meeting: 'rgba(var(--o-deep-blue-6))',
11
11
  };
12
12
 
13
13
  export const INTERVAL_DAY = 0;
@@ -2,8 +2,8 @@ import type { App } from 'vue';
2
2
  import _OMeetingCalendar from './OMeetingCalendar.vue';
3
3
  import _OMeetingForm from './OMeetingForm.vue';
4
4
  import _OMeetingPlayback from './OMeetingPlayback.vue';
5
- import _OMeetingSigCalendar from './OMeetingSigCalendar.vue';
6
- import _OMeetingMyCalendar from './OMeetingMyCalendar.vue';
5
+ import _OSigMeetingCalendar from './OSigMeetingCalendar.vue';
6
+ import _OMyMeetingCalendar from './OMyMeetingCalendar.vue';
7
7
 
8
8
  const OMeetingCalendar = Object.assign(_OMeetingCalendar, {
9
9
  install(app: App) {
@@ -22,15 +22,15 @@ const OMeetingPlayback = Object.assign(_OMeetingPlayback, {
22
22
  },
23
23
  });
24
24
 
25
- const OMeetingSigCalendar = Object.assign(_OMeetingSigCalendar, {
25
+ const OSigMeetingCalendar = Object.assign(_OSigMeetingCalendar, {
26
26
  install(app: App) {
27
- app.component('OMeetingSigCalendar', _OMeetingSigCalendar);
27
+ app.component('OSigMeetingCalendar', _OSigMeetingCalendar);
28
28
  },
29
29
  });
30
30
 
31
- const OMeetingMyCalendar = Object.assign(_OMeetingMyCalendar, {
31
+ const OMyMeetingCalendar = Object.assign(_OMyMeetingCalendar, {
32
32
  install(app: App) {
33
- app.component('OMeetingMyCalendar', _OMeetingMyCalendar);
33
+ app.component('OMyMeetingCalendar', _OMyMeetingCalendar);
34
34
  },
35
35
  });
36
36
 
@@ -38,8 +38,8 @@ export {
38
38
  OMeetingCalendar,
39
39
  OMeetingForm,
40
40
  OMeetingPlayback,
41
- OMeetingSigCalendar,
42
- OMeetingMyCalendar,
41
+ OSigMeetingCalendar,
42
+ OMyMeetingCalendar,
43
43
  };
44
44
 
45
45
  export * from './types';
@@ -0,0 +1,463 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, toRef, watch } from 'vue';
3
+ import { onClickOutside, useDebounceFn } from '@vueuse/core';
4
+
5
+ import SearchImageInput from './internal/SearchImageInput.vue';
6
+ import SearchPanel from './internal/SearchPanel.vue';
7
+ import { useSearchHistory } from './composables/useSearchHistory';
8
+ import { useI18n } from '@/i18n';
9
+ import type {
10
+ OSearchPayload,
11
+ OSearchRecommendItem,
12
+ OSearchUploadImageFn,
13
+ } from './types';
14
+
15
+ export interface OSearchInputPropsT {
16
+ modelValue?: string;
17
+ imageUrl?: string;
18
+ size?: 'small' | 'medium' | 'large';
19
+ placeholder?: string;
20
+ imagePlaceholder?: string;
21
+
22
+ /** ---- Image search ---- */
23
+ enableImageSearch?: boolean;
24
+ uploadImage?: OSearchUploadImageFn;
25
+ maxImageSize?: number;
26
+ imageUploadTooltip?: string;
27
+
28
+ /** ---- Recommendation lists (controlled by parent) ---- */
29
+ suggestItems?: OSearchRecommendItem[];
30
+ onestepItems?: OSearchRecommendItem[];
31
+ suggestTitle?: string;
32
+ onestepTitle?: string;
33
+ noDataText?: string;
34
+ /** When true, show "no data" placeholder for empty suggest while typing */
35
+ showSuggestEmpty?: boolean;
36
+ highlightKeyword?: boolean;
37
+ debounce?: number;
38
+
39
+ /** ---- History ---- */
40
+ enableHistory?: boolean;
41
+ historyItems?: string[];
42
+ maxHistoryCount?: number;
43
+ storeHistory?: boolean;
44
+ storageKey?: string;
45
+ historyTitle?: string;
46
+ /** Auto-record history on search; default true */
47
+ autoSaveHistory?: boolean;
48
+
49
+ /** ---- "Did you mean" list (below input) ---- */
50
+ suggestList?: string[];
51
+ suggestListLabel?: string;
52
+ /** When true, render suggestList items as HTML (with v-html on inert string) */
53
+ allowHtmlInSuggest?: boolean;
54
+
55
+ /** ---- Misc ---- */
56
+ clearable?: boolean;
57
+ closeOnSearch?: boolean;
58
+ closeOnClickOutside?: boolean;
59
+ /** Show dropdown on focus even when empty (history) */
60
+ openOnFocus?: boolean;
61
+ /** Always show image as inline thumbnail inside the input; never expand preview below */
62
+ alwaysInlineThumbnail?: boolean;
63
+ }
64
+
65
+ const props = withDefaults(defineProps<OSearchInputPropsT>(), {
66
+ modelValue: '',
67
+ imageUrl: '',
68
+ size: 'large',
69
+ enableImageSearch: false,
70
+ maxImageSize: 10 * 1024 * 1024,
71
+ suggestItems: () => [],
72
+ onestepItems: () => [],
73
+ showSuggestEmpty: true,
74
+ highlightKeyword: true,
75
+ debounce: 300,
76
+ enableHistory: true,
77
+ historyItems: () => [],
78
+ maxHistoryCount: 6,
79
+ storeHistory: false,
80
+ storageKey: 'search-history',
81
+ autoSaveHistory: true,
82
+ suggestList: () => [],
83
+ allowHtmlInSuggest: false,
84
+ clearable: true,
85
+ closeOnSearch: true,
86
+ closeOnClickOutside: true,
87
+ openOnFocus: true,
88
+ });
89
+
90
+ const emit = defineEmits<{
91
+ (e: 'update:modelValue', val: string): void;
92
+ (e: 'update:imageUrl', url: string): void;
93
+ (e: 'update:historyItems', items: string[]): void;
94
+ (e: 'focus'): void;
95
+ (e: 'blur'): void;
96
+ (e: 'input', val: string): void;
97
+ (e: 'clear'): void;
98
+ (e: 'search', payload: OSearchPayload): void;
99
+ (e: 'recommend-click', item: OSearchRecommendItem): void;
100
+ (e: 'onestep-click', item: OSearchRecommendItem): void;
101
+ (e: 'history-click', val: string): void;
102
+ (e: 'suggest-list-click', val: string): void;
103
+ (e: 'delete-history', items: string[]): void;
104
+ (e: 'delete-history-item', val: string): void;
105
+ (e: 'image-upload-start', file: File): void;
106
+ (e: 'image-upload-success', url: string, file: File): void;
107
+ (e: 'image-upload-error', error: unknown, file: File): void;
108
+ (e: 'image-validate-error', reason: 'size' | 'type', file: File): void;
109
+ }>();
110
+
111
+ const { t } = useI18n();
112
+
113
+ const inputRef = ref<InstanceType<typeof SearchImageInput>>();
114
+ const wrapperRef = ref<HTMLElement | null>(null);
115
+
116
+ const innerValue = computed({
117
+ get: () => props.modelValue,
118
+ set: (val: string) => emit('update:modelValue', val),
119
+ });
120
+
121
+ const isFocus = ref(false);
122
+ const isPreviewOpen = ref(false);
123
+ const justClosedPreview = ref(false);
124
+ const hasInternalImage = ref(false);
125
+
126
+ const showDropdown = computed(() => {
127
+ if (!isFocus.value) return false;
128
+ if (props.imageUrl || hasInternalImage.value) return false;
129
+ const hasSuggest =
130
+ !!innerValue.value &&
131
+ (props.suggestItems.length > 0 || props.onestepItems.length > 0 || props.showSuggestEmpty);
132
+ const hasHistory = props.enableHistory && history.items.value.length > 0 && !innerValue.value;
133
+ if (hasSuggest) return true;
134
+ if (props.openOnFocus && hasHistory) return true;
135
+ return false;
136
+ });
137
+
138
+ // history
139
+ const historyItemsRef = toRef(props, 'historyItems');
140
+ const storageKeyRef = toRef(props, 'storageKey');
141
+ const storeHistoryRef = toRef(props, 'storeHistory');
142
+ const maxHistoryRef = toRef(props, 'maxHistoryCount');
143
+
144
+ const history = useSearchHistory({
145
+ initial: historyItemsRef,
146
+ storageKey: storageKeyRef,
147
+ storeHistory: storeHistoryRef,
148
+ maxHistoryCount: maxHistoryRef,
149
+ onChange: (items) => emit('update:historyItems', items),
150
+ });
151
+
152
+ // debounced input event
153
+ const emitInputDebounced = useDebounceFn((val: string) => {
154
+ emit('input', val);
155
+ }, () => props.debounce);
156
+
157
+ watch(innerValue, (val) => {
158
+ emitInputDebounced(val);
159
+ });
160
+
161
+ // click outside
162
+ onClickOutside(wrapperRef, () => {
163
+ if (!props.closeOnClickOutside) return;
164
+ if (isPreviewOpen.value || justClosedPreview.value) return;
165
+ isFocus.value = false;
166
+ });
167
+
168
+ const handleFocus = () => {
169
+ isFocus.value = true;
170
+ emit('focus');
171
+ };
172
+
173
+ const handleBlur = () => {
174
+ emit('blur');
175
+ };
176
+
177
+ const runSearch = async () => {
178
+ if (inputRef.value?.getIsUploading?.()) {
179
+ await inputRef.value.awaitUpload?.();
180
+ }
181
+ const keyword = innerValue.value.trim();
182
+ const imageUrl = inputRef.value?.getUploadedUrl?.() || props.imageUrl;
183
+
184
+ if (!keyword && !imageUrl) return;
185
+
186
+ if (props.autoSaveHistory && keyword) {
187
+ history.push(keyword);
188
+ }
189
+
190
+ if (props.closeOnSearch) {
191
+ isFocus.value = false;
192
+ }
193
+
194
+ emit('search', { keyword, imageUrl: imageUrl || undefined });
195
+ };
196
+
197
+ const handleClear = () => {
198
+ innerValue.value = '';
199
+ emit('clear');
200
+ };
201
+
202
+ const handleSuggestClick = (item: OSearchRecommendItem) => {
203
+ emit('recommend-click', item);
204
+ innerValue.value = item.key;
205
+ runSearch();
206
+ };
207
+
208
+ const handleOnestepClick = (item: OSearchRecommendItem) => {
209
+ emit('onestep-click', item);
210
+ if (item.path) {
211
+ const url = item.path;
212
+ if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener,noreferrer');
213
+ }
214
+ };
215
+
216
+ const handleHistoryClick = (val: string) => {
217
+ emit('history-click', val);
218
+ innerValue.value = val;
219
+ runSearch();
220
+ };
221
+
222
+ const handleHistoryRemove = (val: string) => {
223
+ history.remove(val);
224
+ emit('delete-history-item', val);
225
+ };
226
+
227
+ const handleHistoryClear = () => {
228
+ const removed = [...history.items.value];
229
+ history.clearAll();
230
+ emit('delete-history', removed);
231
+ };
232
+
233
+ const handleSuggestListClick = (val: string) => {
234
+ // Strip HTML tags when raw HTML is allowed for display
235
+ const text = props.allowHtmlInSuggest ? val.replace(/<[^>]+>/g, '') : val;
236
+ emit('suggest-list-click', text);
237
+ innerValue.value = text;
238
+ runSearch();
239
+ };
240
+
241
+ const onPreviewChange = (visible: boolean) => {
242
+ isPreviewOpen.value = visible;
243
+ if (!visible) {
244
+ justClosedPreview.value = true;
245
+ setTimeout(() => {
246
+ justClosedPreview.value = false;
247
+ }, 100);
248
+ }
249
+ };
250
+
251
+ const showInlineThumbnail = computed(() =>
252
+ !!props.imageUrl && (props.alwaysInlineThumbnail || !isFocus.value)
253
+ );
254
+
255
+ const onImageUploadStart = (file: File) => {
256
+ hasInternalImage.value = true;
257
+ emit('image-upload-start', file);
258
+ };
259
+
260
+ const onImageUploadSuccess = (url: string, file: File) => {
261
+ hasInternalImage.value = false;
262
+ emit('image-upload-success', url, file);
263
+ };
264
+
265
+ const onImageClear = () => {
266
+ hasInternalImage.value = false;
267
+ emit('update:imageUrl', '');
268
+ };
269
+
270
+ defineExpose({
271
+ focus: () => inputRef.value?.focus?.(),
272
+ blur: () => inputRef.value?.blur?.(),
273
+ search: runSearch,
274
+ saveHistory: (val?: string) => history.push(val ?? innerValue.value),
275
+ });
276
+ </script>
277
+
278
+ <template>
279
+ <div ref="wrapperRef" class="o-search-input" :class="{ 'is-focus': isFocus }">
280
+ <div class="o-search-input-box">
281
+ <SearchImageInput
282
+ ref="inputRef"
283
+ v-model="innerValue"
284
+ :image-url="imageUrl"
285
+ :placeholder="placeholder"
286
+ :image-placeholder="imagePlaceholder"
287
+ :size="size"
288
+ :enable-image-search="enableImageSearch"
289
+ :upload-image="uploadImage"
290
+ :max-image-size="maxImageSize"
291
+ :image-upload-tooltip="imageUploadTooltip"
292
+ :expanded="isFocus && !props.alwaysInlineThumbnail"
293
+ :inline-thumbnail="showInlineThumbnail"
294
+ :clearable="clearable"
295
+ class="o-search-input-field"
296
+ @update:imageUrl="(url: string) => emit('update:imageUrl', url)"
297
+ @focus="handleFocus"
298
+ @blur="handleBlur"
299
+ @enter="runSearch"
300
+ @clear="handleClear"
301
+ @image-clear="onImageClear"
302
+ @image-upload-start="onImageUploadStart"
303
+ @image-upload-success="onImageUploadSuccess"
304
+ @image-upload-error="(error: unknown, file: File) => emit('image-upload-error', error, file)"
305
+ @image-validate-error="(reason: 'size' | 'type', file: File) => emit('image-validate-error', reason, file)"
306
+ @preview-change="onPreviewChange"
307
+ >
308
+ <template #prefix><slot name="input-prefix" /></template>
309
+ <template #suffix="slotProps"><slot name="input-suffix" v-bind="slotProps" /></template>
310
+ <template v-if="$slots['image-preview']" #preview="slotProps">
311
+ <slot name="image-preview" v-bind="slotProps" />
312
+ </template>
313
+ </SearchImageInput>
314
+
315
+ <Transition name="o-search-input-dropdown">
316
+ <div v-if="showDropdown" class="o-search-input-dropdown">
317
+ <slot name="dropdown" :keyword="modelValue">
318
+ <SearchPanel
319
+ :keyword="modelValue"
320
+ :onestep-items="onestepItems"
321
+ :onestep-title="onestepTitle"
322
+ :suggest-items="suggestItems"
323
+ :suggest-title="suggestTitle"
324
+ :history-items="enableHistory ? history.items.value : []"
325
+ :history-title="historyTitle"
326
+ :no-data-text="noDataText"
327
+ :highlight-keyword="highlightKeyword"
328
+ :hide-on-keyword="true"
329
+ :show-suggest-empty="showSuggestEmpty"
330
+ history-layout="list"
331
+ @onestep-click="handleOnestepClick"
332
+ @suggest-click="handleSuggestClick"
333
+ @history-click="handleHistoryClick"
334
+ @history-remove="handleHistoryRemove"
335
+ @history-clear="handleHistoryClear"
336
+ >
337
+ <template v-if="$slots['onestep-header']" #onestep-header="slotProps">
338
+ <slot name="onestep-header" v-bind="slotProps" />
339
+ </template>
340
+ <template v-if="$slots['onestep-content']" #onestep-content="slotProps">
341
+ <slot name="onestep-content" v-bind="slotProps" />
342
+ </template>
343
+ <template v-if="$slots['suggest-header']" #suggest-header="slotProps">
344
+ <slot name="suggest-header" v-bind="slotProps" />
345
+ </template>
346
+ <template v-if="$slots['suggest-content']" #suggest-content="slotProps">
347
+ <slot name="suggest-content" v-bind="slotProps" />
348
+ </template>
349
+ <template v-if="$slots['history-header']" #history-header="slotProps">
350
+ <slot name="history-header" v-bind="slotProps" />
351
+ </template>
352
+ <template v-if="$slots['history-content']" #history-content="slotProps">
353
+ <slot name="history-content" v-bind="slotProps" />
354
+ </template>
355
+ </SearchPanel>
356
+ </slot>
357
+ </div>
358
+ </Transition>
359
+ </div>
360
+
361
+ <div v-if="suggestList?.length" class="o-search-input-suggest-list-row">
362
+ <slot name="suggest-list" :items="suggestList">
363
+ <span class="o-search-input-suggest-list-label">{{ suggestListLabel ?? t('search.suggestListLabel') }}</span>
364
+ <ul class="o-search-input-suggest-list">
365
+ <li
366
+ v-for="(item, idx) in suggestList"
367
+ :key="item + idx"
368
+ class="o-search-input-suggest-list-item"
369
+ @click="handleSuggestListClick(item)"
370
+ >
371
+ <span v-if="allowHtmlInSuggest" v-html="item" />
372
+ <span v-else>{{ item }}</span>
373
+ </li>
374
+ </ul>
375
+ </slot>
376
+ </div>
377
+ </div>
378
+ </template>
379
+
380
+ <style lang="scss" scoped>
381
+ .o-search-input {
382
+ position: relative;
383
+ width: 100%;
384
+ background-color: var(--o-color-fill2);
385
+ border-radius: var(--o-radius-xs);
386
+
387
+ // Make large size 48px tall
388
+ :deep(.o-input.o_box-large) {
389
+ --_box-height: 48px;
390
+ }
391
+ }
392
+
393
+ .o-search-input-box {
394
+ position: relative;
395
+ width: 100%;
396
+ }
397
+
398
+ .o-search-input-field {
399
+ width: 100%;
400
+ }
401
+
402
+ .o-search-input-dropdown {
403
+ position: absolute;
404
+ top: calc(100% + 4px);
405
+ left: 0;
406
+ right: 0;
407
+ z-index: 10;
408
+ padding: var(--o-gap-4);
409
+ background-color: var(--o-color-fill2);
410
+ border-radius: var(--o-radius-xs);
411
+ box-shadow: var(--o-shadow-2);
412
+ }
413
+
414
+ .o-search-input-dropdown-enter-active,
415
+ .o-search-input-dropdown-leave-active {
416
+ transition: opacity var(--o-duration-m1), transform var(--o-duration-m1);
417
+ }
418
+
419
+ .o-search-input-dropdown-enter-from,
420
+ .o-search-input-dropdown-leave-to {
421
+ opacity: 0;
422
+ transform: translateY(-4px);
423
+ }
424
+
425
+ .o-search-input-suggest-list-row {
426
+ display: flex;
427
+ margin-top: 8px;
428
+ align-items: center;
429
+ flex-wrap: wrap;
430
+ @include tip1;
431
+ color: var(--o-color-info1);
432
+ }
433
+
434
+ .o-search-input-suggest-list-label {
435
+ color: var(--o-color-info3);
436
+ margin-right: 4px;
437
+ @include tip1;
438
+ }
439
+
440
+ .o-search-input-suggest-list {
441
+ display: flex;
442
+ flex-wrap: wrap;
443
+ align-items: center;
444
+ padding: 0;
445
+ margin: 0;
446
+ list-style: none;
447
+ }
448
+
449
+ .o-search-input-suggest-list-item {
450
+ margin-right: 8px;
451
+ cursor: pointer;
452
+ color: var(--o-color-primary1);
453
+
454
+ :deep(em) {
455
+ color: var(--o-color-primary1);
456
+ font-style: normal;
457
+ }
458
+
459
+ @include hover {
460
+ text-decoration: underline;
461
+ }
462
+ }
463
+ </style>