@opendesign-plus-test/components 0.0.1-rc.82 → 0.0.1-rc.83

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 (218) hide show
  1. package/dist/chunk-OElCookieNotice.cjs.js +1 -1
  2. package/dist/chunk-OElCookieNotice.es.js +89 -77
  3. package/dist/components/activity/index.d.ts +9 -9
  4. package/dist/components/banner/index.d.ts +6 -6
  5. package/dist/components/events/index.d.ts +20 -20
  6. package/dist/components/header/OHeaderMobile.vue.d.ts +22 -22
  7. package/dist/components/header/components/HeaderNavMobile.vue.d.ts +3 -3
  8. package/dist/components/header/index.d.ts +27 -27
  9. package/dist/components/header-search/index.d.ts +13 -13
  10. package/dist/components/meeting/OMeetingForm.vue.d.ts +2 -0
  11. package/dist/components/meeting/OMeetingPlayback.vue.d.ts +6 -6
  12. package/dist/components/meeting/components/OMeetingPlaybackVideo.vue.d.ts +1 -1
  13. package/dist/components/meeting/index.d.ts +37 -31
  14. package/dist/components/meeting/types.d.ts +3 -0
  15. package/dist/components/search/index.d.ts +11 -11
  16. package/dist/components.cjs.js +35 -35
  17. package/dist/components.css +1 -1
  18. package/dist/components.es.js +6822 -6755
  19. package/dist/treeshaking/components/activity/index.d.ts +9 -9
  20. package/dist/treeshaking/components/banner/index.d.ts +6 -6
  21. package/dist/treeshaking/components/events/index.d.ts +20 -20
  22. package/dist/treeshaking/components/header/OHeaderMobile.vue.d.ts +22 -22
  23. package/dist/treeshaking/components/header/components/HeaderNavMobile.vue.d.ts +3 -3
  24. package/dist/treeshaking/components/header/index.d.ts +27 -27
  25. package/dist/treeshaking/components/header-search/index.d.ts +13 -13
  26. package/dist/treeshaking/components/meeting/OMeetingForm.vue.d.ts +2 -0
  27. package/dist/treeshaking/components/meeting/OMeetingForm.vue.mjs +104 -47
  28. package/dist/treeshaking/components/meeting/OMeetingMyCalendar.css +4 -0
  29. package/dist/treeshaking/components/meeting/OMeetingMyCalendar.vue.mjs +28 -12
  30. package/dist/treeshaking/components/meeting/OMeetingPlayback.vue.d.ts +6 -6
  31. package/dist/treeshaking/components/meeting/components/OMeetingPlaybackVideo.vue.d.ts +1 -1
  32. package/dist/treeshaking/components/meeting/index.d.ts +37 -31
  33. package/dist/treeshaking/components/meeting/types.d.ts +3 -0
  34. package/dist/treeshaking/components/search/index.d.ts +11 -11
  35. package/dist/treeshaking/i18n/en.mjs +6 -0
  36. package/dist/treeshaking/i18n/zh.mjs +8 -2
  37. package/package.json +4 -1
  38. package/dist/components/meeting/components/OMeetingCalendarSelector.vue.d.ts +0 -664
  39. package/dist/treeshaking/components/meeting/components/OMeetingCalendarSelector.vue.d.ts +0 -664
  40. package/docs/design.md +0 -27
  41. package/docs/design_banner.md +0 -41
  42. package/docs/design_section.md +0 -27
  43. package/plugins/css-inject-plugin.ts +0 -37
  44. package/scripts/build.js +0 -17
  45. package/scripts/generate-components-index.js +0 -62
  46. package/src/assets/events/svg-icons/icon-checked.svg +0 -3
  47. package/src/assets/events/svg-icons/icon-competition.svg +0 -7
  48. package/src/assets/events/svg-icons/icon-events.svg +0 -4
  49. package/src/assets/events/svg-icons/icon-release.svg +0 -4
  50. package/src/assets/events/svg-icons/icon-summit.svg +0 -4
  51. package/src/assets/meeting/empty.png +0 -0
  52. package/src/assets/meeting/svg-icons/icon-all.svg +0 -4
  53. package/src/assets/meeting/svg-icons/icon-backward.svg +0 -4
  54. package/src/assets/meeting/svg-icons/icon-calendar.svg +0 -3
  55. package/src/assets/meeting/svg-icons/icon-cancel.svg +0 -4
  56. package/src/assets/meeting/svg-icons/icon-captions.svg +0 -4
  57. package/src/assets/meeting/svg-icons/icon-close-captions.svg +0 -6
  58. package/src/assets/meeting/svg-icons/icon-close-fullscreen.svg +0 -6
  59. package/src/assets/meeting/svg-icons/icon-copy.svg +0 -4
  60. package/src/assets/meeting/svg-icons/icon-create.svg +0 -5
  61. package/src/assets/meeting/svg-icons/icon-delete.svg +0 -7
  62. package/src/assets/meeting/svg-icons/icon-empty.svg +0 -65
  63. package/src/assets/meeting/svg-icons/icon-event.svg +0 -4
  64. package/src/assets/meeting/svg-icons/icon-export.svg +0 -3
  65. package/src/assets/meeting/svg-icons/icon-forward.svg +0 -4
  66. package/src/assets/meeting/svg-icons/icon-fullscreen.svg +0 -6
  67. package/src/assets/meeting/svg-icons/icon-help.svg +0 -3
  68. package/src/assets/meeting/svg-icons/icon-important.svg +0 -4
  69. package/src/assets/meeting/svg-icons/icon-info.svg +0 -3
  70. package/src/assets/meeting/svg-icons/icon-meet.svg +0 -4
  71. package/src/assets/meeting/svg-icons/icon-meeting-message.svg +0 -5
  72. package/src/assets/meeting/svg-icons/icon-meeting.svg +0 -4
  73. package/src/assets/meeting/svg-icons/icon-play.svg +0 -5
  74. package/src/assets/meeting/svg-icons/icon-playing-tip.svg +0 -7
  75. package/src/assets/meeting/svg-icons/icon-playing.svg +0 -5
  76. package/src/assets/meeting/svg-icons/icon-question.svg +0 -4
  77. package/src/assets/meeting/svg-icons/icon-sound.svg +0 -5
  78. package/src/assets/meeting/svg-icons/icon-speaker.svg +0 -3
  79. package/src/assets/meeting/svg-icons/icon-summit.svg +0 -4
  80. package/src/assets/meeting/svg-icons/icon-telligent.svg +0 -3
  81. package/src/assets/meeting/svg-icons/icon-tip.svg +0 -3
  82. package/src/assets/meeting/svg-icons/icon-todo.svg +0 -4
  83. package/src/assets/meeting/transparent.png +0 -0
  84. package/src/assets/styles/element-plus.scss +0 -283
  85. package/src/assets/svg-icons/icon-arrow-left.svg +0 -3
  86. package/src/assets/svg-icons/icon-avatar-line.svg +0 -3
  87. package/src/assets/svg-icons/icon-caret-left.svg +0 -3
  88. package/src/assets/svg-icons/icon-caret-right.svg +0 -3
  89. package/src/assets/svg-icons/icon-chevron-down.svg +0 -3
  90. package/src/assets/svg-icons/icon-chevron-right.svg +0 -3
  91. package/src/assets/svg-icons/icon-chevron-up.svg +0 -3
  92. package/src/assets/svg-icons/icon-close.svg +0 -3
  93. package/src/assets/svg-icons/icon-delete-hover.svg +0 -4
  94. package/src/assets/svg-icons/icon-delete.svg +0 -7
  95. package/src/assets/svg-icons/icon-filter.svg +0 -3
  96. package/src/assets/svg-icons/icon-header-back.svg +0 -3
  97. package/src/assets/svg-icons/icon-header-delete.svg +0 -3
  98. package/src/assets/svg-icons/icon-header-menu.svg +0 -3
  99. package/src/assets/svg-icons/icon-header-person.svg +0 -3
  100. package/src/assets/svg-icons/icon-header-search.svg +0 -4
  101. package/src/assets/svg-icons/icon-image-close.svg +0 -4
  102. package/src/assets/svg-icons/icon-image-upload.svg +0 -3
  103. package/src/assets/svg-icons/icon-image-zoomin.svg +0 -3
  104. package/src/assets/svg-icons/icon-loading.svg +0 -4
  105. package/src/assets/svg-icons/icon-locale.svg +0 -3
  106. package/src/assets/svg-icons/icon-log-off.svg +0 -3
  107. package/src/assets/svg-icons/icon-message.svg +0 -3
  108. package/src/assets/svg-icons/icon-moon.svg +0 -3
  109. package/src/assets/svg-icons/icon-outlink.svg +0 -3
  110. package/src/assets/svg-icons/icon-overview.svg +0 -3
  111. package/src/assets/svg-icons/icon-refresh.svg +0 -3
  112. package/src/assets/svg-icons/icon-search.svg +0 -3
  113. package/src/assets/svg-icons/icon-setting.svg +0 -3
  114. package/src/assets/svg-icons/icon-sun.svg +0 -3
  115. package/src/assets/svg-icons/icon-tips.svg +0 -3
  116. package/src/components/activity/OActivityApproval.vue +0 -868
  117. package/src/components/activity/OActivityForm.vue +0 -548
  118. package/src/components/activity/OActivityMyCalendar.vue +0 -1540
  119. package/src/components/activity/composables/useActivityConfig.ts +0 -141
  120. package/src/components/activity/config.ts +0 -1
  121. package/src/components/activity/index.ts +0 -24
  122. package/src/components/activity/types.ts +0 -95
  123. package/src/components/banner/OBanner.vue +0 -288
  124. package/src/components/banner/OBannerContent.vue +0 -175
  125. package/src/components/banner/index.ts +0 -18
  126. package/src/components/banner/types.ts +0 -39
  127. package/src/components/common/AppAvatar.vue +0 -83
  128. package/src/components/common/ClientOnly.vue +0 -13
  129. package/src/components/common/ContentWrapper.vue +0 -82
  130. package/src/components/common/MoreText.vue +0 -124
  131. package/src/components/common/ThFilter.vue +0 -330
  132. package/src/components/config-provider/OPlusConfigProvider.vue +0 -32
  133. package/src/components/config-provider/index.ts +0 -10
  134. package/src/components/cookie-notice/OCookieNotice.vue +0 -575
  135. package/src/components/cookie-notice/index.ts +0 -10
  136. package/src/components/element-plus/OElCookieNotice.vue +0 -603
  137. package/src/components/element-plus/index.ts +0 -3
  138. package/src/components/events/OEventsApply.vue +0 -509
  139. package/src/components/events/OEventsCalendar.vue +0 -600
  140. package/src/components/events/OEventsList.vue +0 -461
  141. package/src/components/events/config.ts +0 -35
  142. package/src/components/events/index.ts +0 -24
  143. package/src/components/events/types.ts +0 -84
  144. package/src/components/events/utils.ts +0 -9
  145. package/src/components/footer/OFooter.vue +0 -577
  146. package/src/components/footer/index.ts +0 -10
  147. package/src/components/header/OHeader.vue +0 -157
  148. package/src/components/header/OHeaderMobile.vue +0 -184
  149. package/src/components/header/components/HeaderContent.vue +0 -1125
  150. package/src/components/header/components/HeaderNav.vue +0 -278
  151. package/src/components/header/components/HeaderNavMobile.vue +0 -380
  152. package/src/components/header/index.ts +0 -18
  153. package/src/components/header/types.ts +0 -96
  154. package/src/components/header-language-switcher/OHeaderLanguageSwitcher.vue +0 -249
  155. package/src/components/header-language-switcher/index.ts +0 -10
  156. package/src/components/header-search/OHeaderSearch.vue +0 -628
  157. package/src/components/header-search/index.ts +0 -10
  158. package/src/components/header-source-code/OHeaderSourceCode.vue +0 -151
  159. package/src/components/header-source-code/index.ts +0 -10
  160. package/src/components/header-theme/OHeaderTheme.vue +0 -132
  161. package/src/components/header-theme/index.ts +0 -10
  162. package/src/components/header-user/OHeaderUser.vue +0 -249
  163. package/src/components/header-user/index.ts +0 -10
  164. package/src/components/meeting/OMeetingCalendar.vue +0 -943
  165. package/src/components/meeting/OMeetingForm.vue +0 -1131
  166. package/src/components/meeting/OMeetingMyCalendar.vue +0 -1628
  167. package/src/components/meeting/OMeetingPlayback.vue +0 -469
  168. package/src/components/meeting/OMeetingSigCalendar.vue +0 -500
  169. package/src/components/meeting/components/OMeetingCalendarList.vue +0 -626
  170. package/src/components/meeting/components/OMeetingCalendarSelector.vue +0 -213
  171. package/src/components/meeting/components/OMeetingDetail.vue +0 -285
  172. package/src/components/meeting/components/OMeetingPlaybackSubtitles.vue +0 -615
  173. package/src/components/meeting/components/OMeetingPlaybackVideo.vue +0 -741
  174. package/src/components/meeting/components/OMeetingSigAside.vue +0 -205
  175. package/src/components/meeting/composables/useMeetingConfig.ts +0 -108
  176. package/src/components/meeting/config.ts +0 -48
  177. package/src/components/meeting/index.ts +0 -45
  178. package/src/components/meeting/types.ts +0 -270
  179. package/src/components/meeting/utils.ts +0 -70
  180. package/src/components/search/OSearchInput.vue +0 -526
  181. package/src/components/search/composables/useImageSearch.ts +0 -157
  182. package/src/components/search/composables/useKeywordHighlight.ts +0 -30
  183. package/src/components/search/composables/useSearchHistory.ts +0 -75
  184. package/src/components/search/index.ts +0 -23
  185. package/src/components/search/internal/HighlightText.vue +0 -37
  186. package/src/components/search/internal/SearchImageInput.vue +0 -498
  187. package/src/components/search/internal/SearchPanel.vue +0 -431
  188. package/src/components/search/types.ts +0 -25
  189. package/src/components/section/OSection.vue +0 -178
  190. package/src/components/section/index.ts +0 -10
  191. package/src/draft/Banner.vue +0 -265
  192. package/src/draft/ButtonCards.vue +0 -106
  193. package/src/draft/Feature.vue +0 -134
  194. package/src/draft/Footer.vue +0 -519
  195. package/src/draft/HorizontalAnchor.vue +0 -165
  196. package/src/draft/ItemSwiper.vue +0 -133
  197. package/src/draft/Logo.vue +0 -141
  198. package/src/draft/LogoCard.vue +0 -75
  199. package/src/draft/LogoV2.vue +0 -19
  200. package/src/draft/MainCard.vue +0 -38
  201. package/src/draft/MultiCard.vue +0 -95
  202. package/src/draft/MultiIconCard.vue +0 -74
  203. package/src/draft/OInfoCard.vue +0 -176
  204. package/src/draft/Process.vue +0 -81
  205. package/src/draft/Section.vue +0 -167
  206. package/src/draft/SingleTabCard.vue +0 -85
  207. package/src/draft/SliderCard.vue +0 -111
  208. package/src/env.d.ts +0 -16
  209. package/src/i18n/en.ts +0 -278
  210. package/src/i18n/index.ts +0 -56
  211. package/src/i18n/zh.ts +0 -268
  212. package/src/index.ts +0 -18
  213. package/src/shared/provide.ts +0 -6
  214. package/src/shims-vue-dompurify-html.d.ts +0 -17
  215. package/src/vue.d.ts +0 -10
  216. package/tsconfig.json +0 -37
  217. package/vite.config.ts +0 -123
  218. package/vite.treeshaking.config.ts +0 -127
@@ -1,70 +0,0 @@
1
- import dayjs, { Dayjs } from 'dayjs';
2
- import { OptionItemT } from './types';
3
-
4
- export const formatDate = (date: undefined | string | Date | Dayjs = new Date(), format: string = 'YYYY/MM/DD'): string => {
5
- let dateStr: any = date;
6
- if (!dateStr) {
7
- dateStr = undefined;
8
- }
9
- if (!dayjs(new Date(dateStr)).isValid()) {
10
- return dateStr as string;
11
- }
12
- return dayjs(new Date(dateStr)).format(format);
13
- };
14
-
15
- export const findLabelFromOptions = (value: string | number, options: OptionItemT[], labelKey: keyof OptionItemT = 'label', valueKey: keyof OptionItemT = 'value') => {
16
- const find = options.find((o) => o[valueKey] === value);
17
- return find?.[labelKey] || value;
18
- };
19
-
20
- export const speakerNum = (val: string) => {
21
- const regex = /\d+/g;
22
- const match = val.match(regex) || '';
23
-
24
- return parseInt(match[0]);
25
- };
26
-
27
- export const getDateNumber = (date: string) => {
28
- const arr: string[] = date.split(':');
29
- if (arr.length > 3 || !arr.length) {
30
- return 0;
31
- }
32
- let res = 0;
33
- let base = 1;
34
- while (arr.length > 0) {
35
- const current = arr.pop();
36
- res += parseInt(current!) * base;
37
- base *= 60;
38
- }
39
- return res;
40
- };
41
-
42
- const formatNumber = (num: number) => {
43
- if (num < 0) return 'Invalid number';
44
- if (num >= 10) return `${ num }`;
45
- return `0${ num }`;
46
- };
47
-
48
- export const formatDateNumber = (num: number) => {
49
- if (num > 24 * 60 * 60) return 'Invalid date';
50
- const h = Math.floor(num / 60 / 60);
51
- const m = Math.floor((num - h * 60 * 60) / 60);
52
- const s = num % 60;
53
- return `${ formatNumber(h) }:${ formatNumber(m) }:${ formatNumber(s) }`;
54
- };
55
-
56
- export const transformTime = (time: string) => {
57
- const arr = time.split(':');
58
- const hh = arr[arr.length - 3] ? parseInt(arr[arr.length - 3]) : 0;
59
- const mm = parseInt(arr[arr.length - 2]);
60
- const ss = parseFloat(arr[arr.length - 1]);
61
-
62
- return hh * 60 * 60 + mm * 60 + ss;
63
- };
64
-
65
- export const transformSeconds = (seconds: number) => {
66
- const hh = Math.floor(seconds / 3600);
67
- const mm = Math.floor((seconds - hh * 3600) / 60);
68
- const ss = Math.floor(seconds - hh * 3600 - mm * 60);
69
- return `${ hh ? `${ hh.toString().padStart(2, '0') }:` : '' }${ mm.toString().padStart(2, '0') }:${ ss.toString().padStart(2, '0') }`;
70
- };
@@ -1,526 +0,0 @@
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
- allowedImageTypes?: string[];
28
-
29
- /** ---- Recommendation lists (controlled by parent) ---- */
30
- suggestItems?: OSearchRecommendItem[];
31
- onestepItems?: OSearchRecommendItem[];
32
- suggestTitle?: string;
33
- onestepTitle?: string;
34
- noDataText?: string;
35
- /** When true, show "no data" placeholder for empty suggest while typing */
36
- showSuggestEmpty?: boolean;
37
- highlightKeyword?: boolean;
38
- debounce?: number;
39
-
40
- /** ---- History ---- */
41
- enableHistory?: boolean;
42
- historyItems?: string[];
43
- maxHistoryCount?: number;
44
- storeHistory?: boolean;
45
- storageKey?: string;
46
- historyTitle?: string;
47
- /** Auto-record history on search; default true */
48
- autoSaveHistory?: boolean;
49
-
50
- /** ---- Hot searches (shown alongside history when no keyword) ---- */
51
- hotItems?: string[];
52
- hotTitle?: string;
53
-
54
- /** ---- "Did you mean" list (below input) ---- */
55
- suggestList?: string[];
56
- suggestListLabel?: string;
57
-
58
- /** ---- Misc ---- */
59
- clearable?: boolean;
60
- closeOnSearch?: boolean;
61
- closeOnClickOutside?: boolean;
62
- /** Show dropdown on focus even when empty (history) */
63
- openOnFocus?: boolean;
64
- /** Always show image as inline thumbnail inside the input; never expand preview below */
65
- alwaysInlineThumbnail?: boolean;
66
- }
67
-
68
- const props = withDefaults(defineProps<OSearchInputPropsT>(), {
69
- modelValue: '',
70
- imageUrl: '',
71
- size: 'large',
72
- enableImageSearch: false,
73
- maxImageSize: 10 * 1024 * 1024,
74
- suggestItems: () => [],
75
- onestepItems: () => [],
76
- showSuggestEmpty: true,
77
- highlightKeyword: true,
78
- debounce: 300,
79
- enableHistory: true,
80
- historyItems: () => [],
81
- maxHistoryCount: 6,
82
- storeHistory: false,
83
- storageKey: 'search-history',
84
- autoSaveHistory: true,
85
- hotItems: () => [],
86
- suggestList: () => [],
87
- clearable: true,
88
- closeOnSearch: true,
89
- closeOnClickOutside: true,
90
- openOnFocus: true,
91
- });
92
-
93
- const emit = defineEmits<{
94
- (e: 'update:modelValue', val: string): void;
95
- (e: 'update:imageUrl', url: string): void;
96
- (e: 'update:historyItems', items: string[]): void;
97
- (e: 'focus'): void;
98
- (e: 'blur'): void;
99
- (e: 'input', val: string): void;
100
- (e: 'clear'): void;
101
- (e: 'search', payload: OSearchPayload): void;
102
- (e: 'recommend-click', item: OSearchRecommendItem): void;
103
- (e: 'onestep-click', item: OSearchRecommendItem): void;
104
- (e: 'history-click', val: string): void;
105
- (e: 'hot-click', val: string): void;
106
- (e: 'suggest-list-click', val: string): void;
107
- (e: 'delete-history', items: string[]): void;
108
- (e: 'delete-history-item', val: string): void;
109
- (e: 'image-upload-start', file: File): void;
110
- (e: 'image-upload-success', url: string, file: File): void;
111
- (e: 'image-upload-error', error: unknown, file: File): void;
112
- (e: 'image-validate-error', reason: 'size' | 'type', file: File): void;
113
- }>();
114
-
115
- const { t } = useI18n();
116
-
117
- type SuggestSegment = { text: string; em: boolean };
118
- function parseSuggest(raw: string): SuggestSegment[] {
119
- const segments: SuggestSegment[] = [];
120
- const re = /<em>(.*?)<\/em>/g;
121
- let last = 0;
122
- let m: RegExpExecArray | null;
123
- while ((m = re.exec(raw)) !== null) {
124
- if (m.index > last) segments.push({ text: raw.slice(last, m.index), em: false });
125
- segments.push({ text: m[1], em: true });
126
- last = m.index + m[0].length;
127
- }
128
- if (last < raw.length) segments.push({ text: raw.slice(last), em: false });
129
- return segments;
130
- }
131
-
132
- const inputRef = ref<InstanceType<typeof SearchImageInput>>();
133
- const wrapperRef = ref<HTMLElement | null>(null);
134
-
135
- const innerValue = computed({
136
- get: () => props.modelValue,
137
- set: (val: string) => emit('update:modelValue', val),
138
- });
139
-
140
- const isFocus = ref(false);
141
- const isPreviewOpen = ref(false);
142
- const justClosedPreview = ref(false);
143
- const hasInternalImage = ref(false);
144
-
145
- const showDropdown = computed(() => {
146
- if (!isFocus.value) return false;
147
- if (props.imageUrl || hasInternalImage.value) return false;
148
- const hasSuggest =
149
- !!innerValue.value &&
150
- (props.suggestItems.length > 0 || props.onestepItems.length > 0 || props.showSuggestEmpty);
151
- const hasHistory = props.enableHistory && history.items.value.length > 0 && !innerValue.value;
152
- const hasHot = props.hotItems.length > 0 && !innerValue.value;
153
- if (hasSuggest) return true;
154
- if (props.openOnFocus && (hasHistory || hasHot)) return true;
155
- return false;
156
- });
157
-
158
- // history
159
- const historyItemsRef = toRef(props, 'historyItems');
160
- const storageKeyRef = toRef(props, 'storageKey');
161
- const storeHistoryRef = toRef(props, 'storeHistory');
162
- const maxHistoryRef = toRef(props, 'maxHistoryCount');
163
-
164
- const history = useSearchHistory({
165
- initial: historyItemsRef,
166
- storageKey: storageKeyRef,
167
- storeHistory: storeHistoryRef,
168
- maxHistoryCount: maxHistoryRef,
169
- onChange: (items) => emit('update:historyItems', items),
170
- });
171
-
172
- // debounced input event
173
- // suppressNextInput: set to true before programmatic value assignments (history/suggest
174
- // clicks) so the watcher-triggered debounce doesn't fire the input event.
175
- let suppressNextInput = false;
176
- const emitInputDebounced = useDebounceFn((val: string) => {
177
- if (suppressNextInput) {
178
- suppressNextInput = false;
179
- return;
180
- }
181
- emit('input', val);
182
- }, () => props.debounce);
183
-
184
- watch(innerValue, (val) => {
185
- emitInputDebounced(val);
186
- });
187
-
188
- // click outside
189
- onClickOutside(wrapperRef, () => {
190
- if (!props.closeOnClickOutside) return;
191
- if (isPreviewOpen.value || justClosedPreview.value) return;
192
- isFocus.value = false;
193
- });
194
-
195
- const handleFocus = () => {
196
- isFocus.value = true;
197
- emit('focus');
198
- };
199
-
200
- const handleBlur = () => {
201
- emit('blur');
202
- };
203
-
204
- const runSearch = async (overrideKeyword?: string) => {
205
- if (inputRef.value?.getIsUploading?.()) {
206
- await inputRef.value.awaitUpload?.();
207
- }
208
- const keyword = overrideKeyword !== undefined ? overrideKeyword.trim() : innerValue.value.trim();
209
- const imageUrl = inputRef.value?.getUploadedUrl?.() || props.imageUrl;
210
-
211
- if (!keyword && !imageUrl) return;
212
-
213
- if (props.autoSaveHistory && keyword) {
214
- history.push(keyword);
215
- }
216
-
217
- if (props.closeOnSearch) {
218
- isFocus.value = false;
219
- }
220
-
221
- emit('search', { keyword, imageUrl: imageUrl || undefined });
222
- };
223
-
224
- const handleClear = () => {
225
- innerValue.value = '';
226
- emit('clear');
227
- };
228
-
229
- const handleSuggestClick = (item: OSearchRecommendItem) => {
230
- emit('recommend-click', item);
231
- suppressNextInput = true;
232
- innerValue.value = item.key;
233
- runSearch(item.key ?? '');
234
- };
235
-
236
- const handleOnestepClick = (item: OSearchRecommendItem) => {
237
- emit('onestep-click', item);
238
- if (item.path) {
239
- const url = item.path;
240
- if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener,noreferrer');
241
- }
242
- };
243
-
244
- const handleHistoryClick = (val: string) => {
245
- emit('history-click', val);
246
- runSearch(val);
247
- };
248
-
249
- const handleHotClick = (val: string) => {
250
- emit('hot-click', val);
251
- runSearch(val);
252
- };
253
-
254
- const handleHistoryRemove = (val: string) => {
255
- history.remove(val);
256
- emit('delete-history-item', val);
257
- };
258
-
259
- const handleHistoryClear = () => {
260
- const removed = [...history.items.value];
261
- history.clearAll();
262
- emit('delete-history', removed);
263
- };
264
-
265
- const handleSuggestListClick = (val: string) => {
266
- const text = val.replace(/<[^>]+>/g, '');
267
- emit('suggest-list-click', text);
268
- suppressNextInput = true;
269
- innerValue.value = text;
270
- runSearch(text);
271
- };
272
-
273
- const onPreviewChange = (visible: boolean) => {
274
- isPreviewOpen.value = visible;
275
- if (!visible) {
276
- justClosedPreview.value = true;
277
- setTimeout(() => {
278
- justClosedPreview.value = false;
279
- }, 100);
280
- }
281
- };
282
-
283
- const showInlineThumbnail = computed(() =>
284
- !!props.imageUrl && (props.alwaysInlineThumbnail || !isFocus.value)
285
- );
286
-
287
- const onImageUploadStart = (file: File) => {
288
- hasInternalImage.value = true;
289
- emit('image-upload-start', file);
290
- };
291
-
292
- const onImageUploadSuccess = (url: string, file: File) => {
293
- hasInternalImage.value = false;
294
- emit('image-upload-success', url, file);
295
- };
296
-
297
- const onImageClear = () => {
298
- hasInternalImage.value = false;
299
- emit('update:imageUrl', '');
300
- };
301
-
302
- defineExpose({
303
- focus: () => inputRef.value?.focus?.(),
304
- blur: () => inputRef.value?.blur?.(),
305
- search: runSearch,
306
- saveHistory: (val?: string) => history.push(val ?? innerValue.value),
307
- });
308
- </script>
309
-
310
- <template>
311
- <div ref="wrapperRef" class="o-search-input" :class="{ 'is-focus': isFocus }">
312
- <div class="o-search-input-box">
313
- <SearchImageInput
314
- ref="inputRef"
315
- v-model="innerValue"
316
- :image-url="imageUrl"
317
- :placeholder="placeholder"
318
- :image-placeholder="imagePlaceholder"
319
- :size="size"
320
- :enable-image-search="enableImageSearch"
321
- :upload-image="uploadImage"
322
- :max-image-size="maxImageSize"
323
- :image-upload-tooltip="imageUploadTooltip"
324
- :allowed-image-types="allowedImageTypes"
325
- :expanded="isFocus && !props.alwaysInlineThumbnail"
326
- :inline-thumbnail="showInlineThumbnail"
327
- :clearable="clearable"
328
- class="o-search-input-field"
329
- @update:imageUrl="(url: string) => emit('update:imageUrl', url)"
330
- @focus="handleFocus"
331
- @blur="handleBlur"
332
- @enter="runSearch"
333
- @clear="handleClear"
334
- @image-clear="onImageClear"
335
- @image-upload-start="onImageUploadStart"
336
- @image-upload-success="onImageUploadSuccess"
337
- @image-upload-error="(error: unknown, file: File) => emit('image-upload-error', error, file)"
338
- @image-validate-error="(reason: 'size' | 'type', file: File) => emit('image-validate-error', reason, file)"
339
- @preview-change="onPreviewChange"
340
- >
341
- <template #prefix><slot name="input-prefix" /></template>
342
- <template #suffix="slotProps"><slot name="input-suffix" v-bind="slotProps" /></template>
343
- <template v-if="$slots['image-preview']" #preview="slotProps">
344
- <slot name="image-preview" v-bind="slotProps" />
345
- </template>
346
- </SearchImageInput>
347
-
348
- <Transition name="o-search-input-dropdown">
349
- <div v-if="showDropdown" class="o-search-input-dropdown">
350
- <slot name="dropdown" :keyword="modelValue">
351
- <SearchPanel
352
- :keyword="modelValue"
353
- :onestep-items="onestepItems"
354
- :onestep-title="onestepTitle"
355
- :suggest-items="suggestItems"
356
- :suggest-title="suggestTitle"
357
- :history-items="enableHistory ? history.items.value : []"
358
- :history-title="historyTitle"
359
- :hot-items="hotItems"
360
- :hot-title="hotTitle"
361
- :no-data-text="noDataText"
362
- :highlight-keyword="highlightKeyword"
363
- :hide-on-keyword="true"
364
- :show-suggest-empty="showSuggestEmpty"
365
- history-layout="list"
366
- @onestep-click="handleOnestepClick"
367
- @suggest-click="handleSuggestClick"
368
- @history-click="handleHistoryClick"
369
- @history-remove="handleHistoryRemove"
370
- @history-clear="handleHistoryClear"
371
- @hot-click="handleHotClick"
372
- >
373
- <template v-if="$slots['onestep-header']" #onestep-header="slotProps">
374
- <slot name="onestep-header" v-bind="slotProps" />
375
- </template>
376
- <template v-if="$slots['onestep-content']" #onestep-content="slotProps">
377
- <slot name="onestep-content" v-bind="slotProps" />
378
- </template>
379
- <template v-if="$slots['suggest-header']" #suggest-header="slotProps">
380
- <slot name="suggest-header" v-bind="slotProps" />
381
- </template>
382
- <template v-if="$slots['suggest-content']" #suggest-content="slotProps">
383
- <slot name="suggest-content" v-bind="slotProps" />
384
- </template>
385
- <template v-if="$slots['history-header']" #history-header="slotProps">
386
- <slot name="history-header" v-bind="slotProps" />
387
- </template>
388
- <template v-if="$slots['history-content']" #history-content="slotProps">
389
- <slot name="history-content" v-bind="slotProps" />
390
- </template>
391
- </SearchPanel>
392
- </slot>
393
- </div>
394
- </Transition>
395
- </div>
396
-
397
- <div v-if="suggestList?.length" class="o-search-input-suggest-list-row">
398
- <slot name="suggest-list" :items="suggestList">
399
- <span class="o-search-input-suggest-list-label">{{ suggestListLabel ?? t('search.suggestListLabel') }}</span>
400
- <ul class="o-search-input-suggest-list">
401
- <li
402
- v-for="(item, idx) in suggestList"
403
- :key="item + idx"
404
- class="o-search-input-suggest-list-item"
405
- @click="handleSuggestListClick(item)"
406
- >
407
- <template v-for="seg in parseSuggest(item)" :key="seg.text + String(seg.em)">
408
- <em v-if="seg.em">{{ seg.text }}</em>
409
- <template v-else>{{ seg.text }}</template>
410
- </template>
411
- </li>
412
- </ul>
413
- </slot>
414
- </div>
415
- </div>
416
- </template>
417
-
418
- <style lang="scss" scoped>
419
- .o-search-input {
420
- position: relative;
421
- width: 100%;
422
- background-color: var(--o-color-fill2);
423
- border-radius: var(--o-radius-xs);
424
-
425
- :deep(.o-input.o_box-large) {
426
- --_box-height: 48px;
427
- }
428
- :deep(.o-search-image-input-icon) {
429
- font-size: 24px;
430
- }
431
- :deep(.o-search-image-input-upload-btn) {
432
- width: 24px;
433
- height: 24px;
434
- }
435
-
436
- @include respond-to('pad_v-pc_s') {
437
- :deep(.o-input.o_box-large) {
438
- --_box-height: 40px;
439
- }
440
- }
441
-
442
- @include respond-to('<=pad_v') {
443
- :deep(.o-input.o_box-large) {
444
- --_box-height: 32px;
445
- }
446
- :deep(.o-search-image-input-icon) {
447
- font-size: 16px;
448
- }
449
- :deep(.o-search-image-input-upload-btn) {
450
- width: 16px;
451
- height: 16px;
452
- }
453
- }
454
- }
455
-
456
- .o-search-input-box {
457
- position: relative;
458
- width: 100%;
459
- }
460
-
461
- .o-search-input-field {
462
- width: 100%;
463
- }
464
-
465
- .o-search-input-dropdown {
466
- position: absolute;
467
- top: calc(100% + 4px);
468
- left: 0;
469
- right: 0;
470
- z-index: 10;
471
- padding: var(--o-gap-4);
472
- background-color: var(--o-color-fill2);
473
- border-radius: var(--o-radius-xs);
474
- box-shadow: var(--o-shadow-2);
475
- }
476
-
477
- .o-search-input-dropdown-enter-active,
478
- .o-search-input-dropdown-leave-active {
479
- transition: opacity var(--o-duration-m1), transform var(--o-duration-m1);
480
- }
481
-
482
- .o-search-input-dropdown-enter-from,
483
- .o-search-input-dropdown-leave-to {
484
- opacity: 0;
485
- transform: translateY(-4px);
486
- }
487
-
488
- .o-search-input-suggest-list-row {
489
- display: flex;
490
- margin-top: 8px;
491
- align-items: center;
492
- flex-wrap: wrap;
493
- @include tip1;
494
- color: var(--o-color-info1);
495
- }
496
-
497
- .o-search-input-suggest-list-label {
498
- color: var(--o-color-info3);
499
- margin-right: 4px;
500
- @include tip1;
501
- }
502
-
503
- .o-search-input-suggest-list {
504
- display: flex;
505
- flex-wrap: wrap;
506
- align-items: center;
507
- padding: 0;
508
- margin: 0;
509
- list-style: none;
510
- }
511
-
512
- .o-search-input-suggest-list-item {
513
- margin-right: 8px;
514
- cursor: pointer;
515
- color: var(--o-color-primary1);
516
-
517
- :deep(em) {
518
- color: var(--o-color-primary1);
519
- font-style: normal;
520
- }
521
-
522
- @include hover {
523
- text-decoration: underline;
524
- }
525
- }
526
- </style>