@opentiny/vue-search-box 0.0.2 → 0.1.1-alpha.1

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 (105) hide show
  1. package/README.md +25 -28
  2. package/README.zh-CN.md +107 -0
  3. package/__tests__/search-box.spec.ts +0 -0
  4. package/{es → dist/es}/index.es.js +5 -1
  5. package/{es → dist/es}/index.vue.es2.js +1 -12
  6. package/{lib → dist/lib}/index.cjs.js +4 -0
  7. package/{lib → dist/lib}/index.vue.cjs2.js +53 -64
  8. package/package.json +66 -64
  9. package/scripts/pre-release.cjs +8 -0
  10. package/src/composables/use-checkbox.ts +90 -0
  11. package/src/composables/use-custom.ts +53 -0
  12. package/src/composables/use-datepicker.ts +90 -0
  13. package/src/composables/use-dropdown.ts +251 -0
  14. package/src/composables/use-edit.ts +119 -0
  15. package/src/composables/use-init.ts +69 -0
  16. package/src/composables/use-match.ts +193 -0
  17. package/src/composables/use-num-range.ts +86 -0
  18. package/src/composables/use-placeholder.ts +43 -0
  19. package/src/composables/use-tag.ts +54 -0
  20. package/src/index.less +376 -0
  21. package/src/index.ts +13 -0
  22. package/src/index.type.ts +192 -0
  23. package/src/index.vue +1138 -0
  24. package/src/smb-theme.ts +15 -0
  25. package/src/theme.json +135 -0
  26. package/src/utils/clone.ts +37 -0
  27. package/src/utils/date.ts +724 -0
  28. package/src/utils/dropdown.ts +27 -0
  29. package/src/utils/en_US.ts +41 -0
  30. package/src/utils/index.ts +11 -0
  31. package/src/utils/tag.ts +80 -0
  32. package/src/utils/type.ts +6 -0
  33. package/src/utils/validate.ts +234 -0
  34. package/src/utils/zh_CN.ts +41 -0
  35. package/src/vars.less +56 -0
  36. package/vite.config.theme.ts +20 -0
  37. package/vite.config.ts +60 -0
  38. /package/{es → dist/es}/composables/use-checkbox.es.js +0 -0
  39. /package/{es → dist/es}/composables/use-custom.es.js +0 -0
  40. /package/{es → dist/es}/composables/use-datepicker.es.js +0 -0
  41. /package/{es → dist/es}/composables/use-dropdown.es.js +0 -0
  42. /package/{es → dist/es}/composables/use-edit.es.js +0 -0
  43. /package/{es → dist/es}/composables/use-init.es.js +0 -0
  44. /package/{es → dist/es}/composables/use-match.es.js +0 -0
  45. /package/{es → dist/es}/composables/use-num-range.es.js +0 -0
  46. /package/{es → dist/es}/composables/use-placeholder.es.js +0 -0
  47. /package/{es → dist/es}/composables/use-tag.es.js +0 -0
  48. /package/{es → dist/es}/index-VrLZbD8H.css +0 -0
  49. /package/{es → dist/es}/index.type.es.js +0 -0
  50. /package/{es → dist/es}/index.vue.es.js +0 -0
  51. /package/{es → dist/es}/smb-theme.es.js +0 -0
  52. /package/{es → dist/es}/utils/clone.es.js +0 -0
  53. /package/{es → dist/es}/utils/date.es.js +0 -0
  54. /package/{es → dist/es}/utils/dropdown.es.js +0 -0
  55. /package/{es → dist/es}/utils/en_US.es.js +0 -0
  56. /package/{es → dist/es}/utils/index.es.js +0 -0
  57. /package/{es → dist/es}/utils/tag.es.js +0 -0
  58. /package/{es → dist/es}/utils/type.es.js +0 -0
  59. /package/{es → dist/es}/utils/validate.es.js +0 -0
  60. /package/{es → dist/es}/utils/zh_CN.es.js +0 -0
  61. /package/{index.css → dist/index.css} +0 -0
  62. /package/{lib → dist/lib}/composables/use-checkbox.cjs.js +0 -0
  63. /package/{lib → dist/lib}/composables/use-custom.cjs.js +0 -0
  64. /package/{lib → dist/lib}/composables/use-datepicker.cjs.js +0 -0
  65. /package/{lib → dist/lib}/composables/use-dropdown.cjs.js +0 -0
  66. /package/{lib → dist/lib}/composables/use-edit.cjs.js +0 -0
  67. /package/{lib → dist/lib}/composables/use-init.cjs.js +0 -0
  68. /package/{lib → dist/lib}/composables/use-match.cjs.js +0 -0
  69. /package/{lib → dist/lib}/composables/use-num-range.cjs.js +0 -0
  70. /package/{lib → dist/lib}/composables/use-placeholder.cjs.js +0 -0
  71. /package/{lib → dist/lib}/composables/use-tag.cjs.js +0 -0
  72. /package/{lib → dist/lib}/index-VrLZbD8H.css +0 -0
  73. /package/{lib → dist/lib}/index.type.cjs.js +0 -0
  74. /package/{lib → dist/lib}/index.vue.cjs.js +0 -0
  75. /package/{lib → dist/lib}/smb-theme.cjs.js +0 -0
  76. /package/{lib → dist/lib}/utils/clone.cjs.js +0 -0
  77. /package/{lib → dist/lib}/utils/date.cjs.js +0 -0
  78. /package/{lib → dist/lib}/utils/dropdown.cjs.js +0 -0
  79. /package/{lib → dist/lib}/utils/en_US.cjs.js +0 -0
  80. /package/{lib → dist/lib}/utils/index.cjs.js +0 -0
  81. /package/{lib → dist/lib}/utils/tag.cjs.js +0 -0
  82. /package/{lib → dist/lib}/utils/type.cjs.js +0 -0
  83. /package/{lib → dist/lib}/utils/validate.cjs.js +0 -0
  84. /package/{lib → dist/lib}/utils/zh_CN.cjs.js +0 -0
  85. /package/{types → dist/types}/composables/use-checkbox.d.ts +0 -0
  86. /package/{types → dist/types}/composables/use-custom.d.ts +0 -0
  87. /package/{types → dist/types}/composables/use-datepicker.d.ts +0 -0
  88. /package/{types → dist/types}/composables/use-dropdown.d.ts +0 -0
  89. /package/{types → dist/types}/composables/use-edit.d.ts +0 -0
  90. /package/{types → dist/types}/composables/use-init.d.ts +0 -0
  91. /package/{types → dist/types}/composables/use-match.d.ts +0 -0
  92. /package/{types → dist/types}/composables/use-num-range.d.ts +0 -0
  93. /package/{types → dist/types}/composables/use-placeholder.d.ts +0 -0
  94. /package/{types → dist/types}/composables/use-tag.d.ts +0 -0
  95. /package/{types → dist/types}/index.type.d.ts +0 -0
  96. /package/{types → dist/types}/smb-theme.d.ts +0 -0
  97. /package/{types → dist/types}/utils/clone.d.ts +0 -0
  98. /package/{types → dist/types}/utils/date.d.ts +0 -0
  99. /package/{types → dist/types}/utils/dropdown.d.ts +0 -0
  100. /package/{types → dist/types}/utils/en_US.d.ts +0 -0
  101. /package/{types → dist/types}/utils/index.d.ts +0 -0
  102. /package/{types → dist/types}/utils/tag.d.ts +0 -0
  103. /package/{types → dist/types}/utils/type.d.ts +0 -0
  104. /package/{types → dist/types}/utils/validate.d.ts +0 -0
  105. /package/{types → dist/types}/utils/zh_CN.d.ts +0 -0
@@ -0,0 +1,193 @@
1
+ import { watch, ref } from 'vue'
2
+ import Loading from '@opentiny/vue-loading'
3
+ import { debounce } from '../utils/index'
4
+ import { hasTagItem, createNewTag, getTagId, emitChangeModelEvent } from '../utils/tag'
5
+ import { showDropdown } from '../utils/dropdown'
6
+
7
+ const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
8
+
9
+ const getHighlightMatch = (labelRegex, label, valueLength) => {
10
+ const match = []
11
+ let lastIndex
12
+ while (labelRegex.exec(label) !== null) {
13
+ lastIndex = labelRegex.lastIndex
14
+ const startIndex = lastIndex - valueLength
15
+ const prevMatch = label.slice(0, startIndex)
16
+ const currentMatch = [label.slice(startIndex, lastIndex)]
17
+ prevMatch && match.push(prevMatch)
18
+ match.push(currentMatch)
19
+ }
20
+ if (lastIndex < label.length) {
21
+ match.push(label.slice(lastIndex))
22
+ }
23
+
24
+ return match
25
+ }
26
+
27
+ export function useMatch({ props, state, emits }) {
28
+ const loadingInstance = ref(null)
29
+ watch(
30
+ () => state.inputValue,
31
+ (newInput) => {
32
+ if (!newInput.trim()) {
33
+ state.isShowDropdown = true
34
+ } else {
35
+ state.isShowDropdown = false
36
+ }
37
+ showDropdown(state, false)
38
+ }
39
+ )
40
+
41
+ const getMatchList = async (keyword: string) => {
42
+ !loadingInstance.value &&
43
+ (loadingInstance.value = Loading.service({
44
+ target: document.getElementById('potential-loading')
45
+ }))
46
+ state.potentialOptions = await props.potentialOptions.getMatchList(keyword)
47
+ loadingInstance.value && loadingInstance.value.close()
48
+ if (state.potentialOptions.length) {
49
+ state.isShowDropdown = true
50
+ }
51
+ showDropdown(state, state.isShowDropdown)
52
+ }
53
+
54
+ const handleSearch = (e) => {
55
+ const { recordItems, propItem } = state
56
+ const inputValue = e.target.value.trim()
57
+ const { maxlength } = props
58
+
59
+ if (maxlength && maxlength < inputValue.length) {
60
+ emits('exceed', maxlength)
61
+ return
62
+ }
63
+
64
+ if (inputValue.length === 0) {
65
+ return
66
+ }
67
+
68
+ Object.keys(state.matchItems).forEach((key) => {
69
+ state.matchItems[key].attr = []
70
+ state.matchItems[key].attrValue = []
71
+ })
72
+ state.isShowDropdown = false
73
+
74
+ const value = escapeRegExp(inputValue)
75
+ const patt = new RegExp(value, 'i')
76
+ const hasItem =
77
+ propItem.label || !value ? null : recordItems.find((item) => item.type === 'map' && patt.test(item.label))
78
+ if (hasItem) {
79
+ state.propItem.label = hasItem.label
80
+ state.inputValue = ''
81
+ state.prevItem = hasItem
82
+ state.backupPrevItem = hasItem
83
+ state.backupList = hasItem.options || []
84
+ return
85
+ }
86
+
87
+ state.filterList = state.backupList.filter((item) => {
88
+ if (patt.test(item.label)) {
89
+ delete item.isFilter
90
+ if (hasTagItem(state, item.label)) {
91
+ state.checkboxGroup.push(`${state.prevItem.label}${item.label}`)
92
+ }
93
+
94
+ return true
95
+ } else {
96
+ item.isFilter = true
97
+ return false
98
+ }
99
+ })
100
+
101
+ const labelRegex = new RegExp(value, 'ig')
102
+ const valueLength = inputValue.length
103
+
104
+ // 有label,只在backupList搜索
105
+ if (state.propItem.label) {
106
+ state.backupList.forEach((item) => {
107
+ const itemLabel = item.label
108
+ if (patt.test(itemLabel)) {
109
+ item.match = getHighlightMatch(labelRegex, itemLabel, valueLength)
110
+ item.isFilter = false
111
+ state.isShowDropdown = true
112
+ } else {
113
+ item.isFilter = true
114
+ }
115
+ })
116
+ return
117
+ }
118
+
119
+ // 无label,需要全局搜
120
+ for (const item of recordItems) {
121
+ const { options = [], groupKey = '0', ...rest } = item
122
+ const itemLabel = rest.label
123
+ if (patt.test(itemLabel)) {
124
+ const match = getHighlightMatch(labelRegex, itemLabel, valueLength)
125
+ state.matchItems[groupKey].attr.push({ ...item, match })
126
+ state.isShowDropdown = true
127
+ }
128
+ for (const option of options) {
129
+ const optionLabel = state.propItem.label ? itemLabel : `${itemLabel}:${option.label}`
130
+ if (patt.test(optionLabel)) {
131
+ const match = getHighlightMatch(labelRegex, optionLabel, valueLength)
132
+ state.matchItems[groupKey].attrValue.push({
133
+ ...option,
134
+ ...rest,
135
+ value: option.label,
136
+ match
137
+ })
138
+ state.isShowDropdown = true
139
+ }
140
+ }
141
+ }
142
+
143
+ if (value && props.potentialOptions && props.potentialOptions.getMatchList) {
144
+ getMatchList(value)
145
+ } else {
146
+ showDropdown(state, state.isShowDropdown)
147
+ }
148
+ }
149
+
150
+ const handleInput = debounce(handleSearch, 500)
151
+
152
+ const resetBackupList = () => {
153
+ state.backupList.forEach((item) => item.isFilter && delete item.isFilter)
154
+ }
155
+
156
+ const selectFirstMap = (item, isFirst) => {
157
+ const { options } = item
158
+ const { prevItem, propItem } = state
159
+ if (options) {
160
+ state.propItem.value = `${item.label}=`
161
+ state.isShowTagKey = false
162
+ state.inputValue = ''
163
+ state.backupList = item.options || []
164
+ resetBackupList()
165
+
166
+ state.backupList.forEach((subItem) => {
167
+ const value = propItem.value + subItem.label
168
+ subItem.isChecked = hasTagItem(state, value)
169
+ })
170
+ } else {
171
+ if (item.isChecked) {
172
+ return
173
+ }
174
+
175
+ state.isShowTagKey = true
176
+ resetBackupList()
177
+ const { field, type } = prevItem
178
+ const value = propItem.value + item.label
179
+ const id = getTagId(props, prevItem, item)
180
+ const newTag = createNewTag({ type, field, label: propItem.label, value, ...id })
181
+ const tagList = [newTag]
182
+ emitChangeModelEvent({ emits, state, tagList })
183
+ }
184
+ if (isFirst) {
185
+ showDropdown(state)
186
+ }
187
+ }
188
+
189
+ return {
190
+ handleInput,
191
+ selectFirstMap
192
+ }
193
+ }
@@ -0,0 +1,86 @@
1
+ import { showDropdown } from '../utils/dropdown'
2
+ import { getVerifyNumTag } from '../utils/validate'
3
+ import { emitChangeModelEvent } from '../utils/tag'
4
+
5
+ export function useNumRange({ props, state, t, emits }) {
6
+ const { instance } = state
7
+ const sizeChange = async (confirm: boolean) => {
8
+ if (!confirm) {
9
+ state.propItem.label = ''
10
+ return
11
+ }
12
+
13
+ const newTag = await getVerifyNumTag(instance, state, props)
14
+ if (newTag) {
15
+ const newValue = props.modelValue.filter((prev) => prev.type !== newTag.type || prev.field !== newTag.field)
16
+ newValue.push(newTag)
17
+ emitChangeModelEvent({ emits, state, newValue })
18
+ showDropdown(state, false)
19
+ } else {
20
+ showDropdown(state)
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 创建 type=dateRange 和 type = datetimeRange 的校验规则
26
+ * @param startKey 校验的开始日期名
27
+ * @param endKey 校验的结束日期名
28
+ */
29
+ const createDateRules = (item, startKey, endKey) => {
30
+ const { maxTimeLength = 0 } = item
31
+ return {
32
+ [startKey]: {
33
+ validator: (rule, value, cb) => {
34
+ if (maxTimeLength > 0 && !value) {
35
+ cb(new Error(t('tvp.tvpSearchbox.notBeNull')))
36
+ } else if (!value && !state[endKey]) {
37
+ cb(new Error(t('tvp.tvpSearchbox.rangeDateTitle')))
38
+ } else {
39
+ cb()
40
+ }
41
+ }
42
+ },
43
+ [endKey]: {
44
+ validator: (rule, value, cb) => {
45
+ if (maxTimeLength > 0 && !value) {
46
+ cb(new Error(t('tvp.tvpSearchbox.notBeNull')))
47
+ } else if (!value && !state[startKey]) {
48
+ cb(new Error(t('tvp.tvpSearchbox.rangeDateTitle')))
49
+ } else {
50
+ cb()
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ const initFormRule = () => {
58
+ let dateRules = {},
59
+ datetimeRules = {}
60
+ props.items.forEach((item) => {
61
+ if (item.type === 'dateRange') {
62
+ dateRules = createDateRules(item, 'startDate', 'endDate')
63
+ }
64
+
65
+ if (item.type === 'datetimeRange') {
66
+ datetimeRules = createDateRules(item, 'startDateTime', 'endDateTime')
67
+ }
68
+ })
69
+ state.formRules = {
70
+ ...dateRules,
71
+ ...datetimeRules
72
+ }
73
+ if (props.editable) {
74
+ state.formRules.inputEditValue = {
75
+ required: true,
76
+ message: t('tvp.tvpSearchbox.notBeNull'),
77
+ trigger: ['change', 'blur']
78
+ }
79
+ }
80
+ }
81
+
82
+ return {
83
+ initFormRule,
84
+ sizeChange
85
+ }
86
+ }
@@ -0,0 +1,43 @@
1
+ import { ref, watch } from 'vue'
2
+
3
+ export function usePlaceholder({ props, state, t }) {
4
+ const placeholder = ref(props.emptyPlaceholder)
5
+
6
+ const setPlaceholder = (placeholderValue: string) => {
7
+ placeholder.value = placeholderValue
8
+ }
9
+
10
+ if (props.modelValue.length > 0) {
11
+ setPlaceholder(t('tvp.tvpSearchbox.addPlaceholder'))
12
+ }
13
+
14
+ watch(
15
+ () => state.propItem.label,
16
+ (newValue) => {
17
+ if (newValue) {
18
+ const { placeholder, type } = state.prevItem
19
+ if (placeholder) {
20
+ setPlaceholder(placeholder)
21
+ } else if (type === 'map') {
22
+ setPlaceholder(t('tvp.tvpSearchbox.tagPlaceholder'))
23
+ } else if (state.backupList.length) {
24
+ setPlaceholder(t('tvp.tvpSearchbox.dynamicPlaceholder', { newValue }))
25
+ } else {
26
+ setPlaceholder(t('tvp.tvpSearchbox.addPlaceholder'))
27
+ }
28
+ } else {
29
+ if (props.modelValue.length > 0) {
30
+ setPlaceholder(t('tvp.tvpSearchbox.addPlaceholder'))
31
+ } else {
32
+ setPlaceholder(props.emptyPlaceholder)
33
+ }
34
+ !state.hiden && (state.hiden = true)
35
+ }
36
+ }
37
+ )
38
+
39
+ return {
40
+ placeholder,
41
+ setPlaceholder
42
+ }
43
+ }
@@ -0,0 +1,54 @@
1
+ import { ref } from 'vue'
2
+ import { showDropdown } from '../utils/dropdown'
3
+ import { emitChangeModelEvent } from '../utils/tag'
4
+
5
+ export function useTag({ props, state, emits }) {
6
+ const lastInputValue = ref(state.inputValue)
7
+
8
+ const changeIsChecked = (tag) => {
9
+ if (tag) {
10
+ const parent = state.recordItems.find((item) => item.label === tag.label)
11
+ if (parent && parent.options) {
12
+ const child = parent.options.find((item) => item.label === tag.value)
13
+ child && (child.isChecked = false)
14
+ }
15
+ }
16
+ }
17
+
18
+ const deleteTag = (tag) => {
19
+ changeIsChecked(tag)
20
+ const newValue = props.modelValue.filter((item) => item !== tag)
21
+ emitChangeModelEvent({ emits, state, newValue })
22
+ }
23
+
24
+ const clearTag = () => {
25
+ props.modelValue.forEach((item) => changeIsChecked(item))
26
+ state.propItem = {}
27
+ state.inputValue = ''
28
+ emitChangeModelEvent({ emits, state, newValue: [] })
29
+ showDropdown(state, false)
30
+ }
31
+
32
+ const backspaceDeleteTag = () => {
33
+ if (state.inputValue) {
34
+ return
35
+ }
36
+ if (state.propItem.label) {
37
+ state.propItem = {}
38
+ return
39
+ }
40
+ if (lastInputValue.value === '' && state.inputValue === '') {
41
+ const lastIndex = props.modelValue.length - 1
42
+ changeIsChecked(props.modelValue[lastIndex])
43
+ const newValue = state.innerModelValue.slice(0, props.modelValue.length - 1)
44
+ emitChangeModelEvent({ emits, state, newValue })
45
+ }
46
+ lastInputValue.value = state.inputValue
47
+ }
48
+
49
+ return {
50
+ deleteTag,
51
+ clearTag,
52
+ backspaceDeleteTag
53
+ }
54
+ }