@globalbrain/sefirot 2.0.0-draft.8 → 2.1.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.
Files changed (182) hide show
  1. package/README.md +6 -6
  2. package/lib/components/SAvatar.vue +17 -17
  3. package/lib/components/SButton.vue +520 -276
  4. package/lib/components/SButtonGroup.vue +149 -0
  5. package/lib/components/SDropdown.vue +26 -150
  6. package/lib/components/SDropdownSection.vue +48 -0
  7. package/lib/components/SDropdownSectionFilter.vue +190 -0
  8. package/lib/components/SDropdownSectionFilterItem.vue +21 -0
  9. package/lib/components/SDropdownSectionFilterItemAvatar.vue +31 -0
  10. package/lib/components/SDropdownSectionFilterItemText.vue +20 -0
  11. package/lib/components/SDropdownSectionMenu.vue +39 -0
  12. package/lib/components/SIcon.vue +13 -0
  13. package/lib/components/SInputBase.vue +31 -31
  14. package/lib/components/SInputCheckbox.vue +4 -3
  15. package/lib/components/SInputCheckboxes.vue +74 -0
  16. package/lib/components/SInputDate.vue +182 -0
  17. package/lib/components/SInputDropdown.vue +159 -157
  18. package/lib/components/SInputDropdownItem.vue +46 -48
  19. package/lib/components/SInputDropdownItemAvatar.vue +99 -0
  20. package/lib/components/SInputDropdownItemText.vue +79 -16
  21. package/lib/components/SInputFile.vue +56 -60
  22. package/lib/components/SInputHMS.vue +120 -110
  23. package/lib/components/SInputNumber.vue +38 -9
  24. package/lib/components/SInputRadio.vue +39 -36
  25. package/lib/components/SInputRadios.vue +40 -53
  26. package/lib/components/SInputSelect.vue +7 -6
  27. package/lib/components/SInputSwitch.vue +193 -0
  28. package/lib/components/SInputSwitches.vue +88 -0
  29. package/lib/components/SInputText.vue +207 -62
  30. package/lib/components/SInputTextarea.vue +46 -32
  31. package/lib/components/SInputYMD.vue +123 -126
  32. package/lib/components/SMarkdown.vue +52 -0
  33. package/lib/components/SModal.vue +33 -57
  34. package/lib/components/SMount.vue +19 -0
  35. package/lib/components/SSheet.vue +50 -55
  36. package/lib/components/SSheetFooter.vue +1 -1
  37. package/lib/components/SSheetFooterAction.vue +24 -17
  38. package/lib/components/SSheetFooterActions.vue +1 -4
  39. package/lib/components/SSheetForm.vue +15 -0
  40. package/lib/components/SSheetMedium.vue +8 -10
  41. package/lib/components/SSheetTitle.vue +7 -14
  42. package/lib/components/SSnackbar.vue +58 -47
  43. package/lib/components/{SPortalSnackbars.vue → SSnackbars.vue} +17 -20
  44. package/lib/components/{icons/SIconPreloader.vue → SSpinner.vue} +5 -4
  45. package/lib/components/SStep.vue +107 -0
  46. package/lib/components/SSteps.vue +59 -0
  47. package/lib/components/STable.vue +242 -0
  48. package/lib/components/STableCell.vue +82 -0
  49. package/lib/components/STableCellAvatar.vue +69 -0
  50. package/lib/components/STableCellAvatars.vue +93 -0
  51. package/lib/components/STableCellDay.vue +40 -0
  52. package/lib/components/STableCellPill.vue +84 -0
  53. package/lib/components/STableCellText.vue +103 -0
  54. package/lib/components/STableColumn.vue +255 -0
  55. package/lib/components/STableFooter.vue +115 -0
  56. package/lib/components/STableHeader.vue +74 -0
  57. package/lib/components/STableItem.vue +38 -0
  58. package/lib/components/STooltip.vue +112 -0
  59. package/lib/composables/Dropdown.ts +40 -99
  60. package/lib/composables/Form.ts +21 -18
  61. package/lib/composables/Grid.ts +117 -0
  62. package/lib/composables/Markdown.ts +138 -0
  63. package/lib/composables/Step.ts +7 -0
  64. package/lib/composables/Table.ts +103 -0
  65. package/lib/composables/Tooltip.ts +91 -0
  66. package/lib/composables/Validation.ts +5 -9
  67. package/lib/composables/markdown/LinkPlugin.ts +45 -0
  68. package/lib/mixins/Sheet.ts +5 -3
  69. package/lib/stores/Snackbars.ts +48 -0
  70. package/lib/{assets/styles → styles}/base.css +0 -0
  71. package/lib/{assets/styles → styles}/bootstrap.css +1 -0
  72. package/lib/{assets/styles → styles}/variables.css +55 -48
  73. package/lib/support/Day.ts +8 -0
  74. package/lib/support/Num.ts +3 -0
  75. package/lib/support/Time.ts +5 -2
  76. package/lib/support/Utils.ts +4 -3
  77. package/lib/types/shims.d.ts +3 -0
  78. package/lib/validation/validators/requiredYmd.ts +1 -1
  79. package/lib/validation/validators/ymd.ts +4 -4
  80. package/package.json +59 -37
  81. package/CHANGELOG.md +0 -47
  82. package/lib/.DS_Store +0 -0
  83. package/lib/components/.DS_Store +0 -0
  84. package/lib/components/SDialog.vue +0 -140
  85. package/lib/components/SDropdownItem.vue +0 -78
  86. package/lib/components/SDropdownItemText.vue +0 -22
  87. package/lib/components/SDropdownItemUser.vue +0 -40
  88. package/lib/components/SInputDropdownItemTextTag.vue +0 -94
  89. package/lib/components/SInputDropdownItemUser.vue +0 -41
  90. package/lib/components/SInputDropdownItemUserTag.vue +0 -100
  91. package/lib/components/SPortalModals.vue +0 -74
  92. package/lib/components/icons/.DS_Store +0 -0
  93. package/lib/components/icons/SIconActivity.vue +0 -5
  94. package/lib/components/icons/SIconArrowDown.vue +0 -5
  95. package/lib/components/icons/SIconArrowLeft.vue +0 -5
  96. package/lib/components/icons/SIconArrowRight.vue +0 -5
  97. package/lib/components/icons/SIconArrowUp.vue +0 -5
  98. package/lib/components/icons/SIconBarChart.vue +0 -7
  99. package/lib/components/icons/SIconBriefcase.vue +0 -5
  100. package/lib/components/icons/SIconBuilding.vue +0 -5
  101. package/lib/components/icons/SIconCalendar.vue +0 -5
  102. package/lib/components/icons/SIconCheck.vue +0 -5
  103. package/lib/components/icons/SIconCheckCircle.vue +0 -6
  104. package/lib/components/icons/SIconCheckCircleThin.vue +0 -6
  105. package/lib/components/icons/SIconCheckSquare.vue +0 -6
  106. package/lib/components/icons/SIconChevronDown.vue +0 -5
  107. package/lib/components/icons/SIconChevronLeft.vue +0 -5
  108. package/lib/components/icons/SIconChevronRight.vue +0 -5
  109. package/lib/components/icons/SIconChevronUp.vue +0 -5
  110. package/lib/components/icons/SIconClock.vue +0 -6
  111. package/lib/components/icons/SIconCode.vue +0 -6
  112. package/lib/components/icons/SIconDatabase.vue +0 -5
  113. package/lib/components/icons/SIconDollarSign.vue +0 -5
  114. package/lib/components/icons/SIconDownload.vue +0 -6
  115. package/lib/components/icons/SIconDownloadCloud.vue +0 -6
  116. package/lib/components/icons/SIconEdit.vue +0 -6
  117. package/lib/components/icons/SIconEdit2.vue +0 -5
  118. package/lib/components/icons/SIconEdit3.vue +0 -6
  119. package/lib/components/icons/SIconEdit3Off.vue +0 -6
  120. package/lib/components/icons/SIconExternalLink.vue +0 -6
  121. package/lib/components/icons/SIconEye.vue +0 -6
  122. package/lib/components/icons/SIconFile.vue +0 -5
  123. package/lib/components/icons/SIconFilePlus.vue +0 -6
  124. package/lib/components/icons/SIconFileText.vue +0 -8
  125. package/lib/components/icons/SIconFlag.vue +0 -5
  126. package/lib/components/icons/SIconGitBranch.vue +0 -5
  127. package/lib/components/icons/SIconGitCommit.vue +0 -5
  128. package/lib/components/icons/SIconGitPullRequest.vue +0 -6
  129. package/lib/components/icons/SIconGlobe.vue +0 -5
  130. package/lib/components/icons/SIconGrab.vue +0 -10
  131. package/lib/components/icons/SIconGrid.vue +0 -8
  132. package/lib/components/icons/SIconHome.vue +0 -5
  133. package/lib/components/icons/SIconImage.vue +0 -6
  134. package/lib/components/icons/SIconInbox.vue +0 -5
  135. package/lib/components/icons/SIconInfo.vue +0 -7
  136. package/lib/components/icons/SIconLayout.vue +0 -5
  137. package/lib/components/icons/SIconList.vue +0 -10
  138. package/lib/components/icons/SIconLock.vue +0 -5
  139. package/lib/components/icons/SIconLogout.vue +0 -6
  140. package/lib/components/icons/SIconMail.vue +0 -6
  141. package/lib/components/icons/SIconMapPin.vue +0 -6
  142. package/lib/components/icons/SIconMoon.vue +0 -5
  143. package/lib/components/icons/SIconMoreHorizontal.vue +0 -7
  144. package/lib/components/icons/SIconMoreVertical.vue +0 -7
  145. package/lib/components/icons/SIconPauseFill.vue +0 -6
  146. package/lib/components/icons/SIconPlayCircle.vue +0 -6
  147. package/lib/components/icons/SIconPlayFill.vue +0 -5
  148. package/lib/components/icons/SIconPlus.vue +0 -5
  149. package/lib/components/icons/SIconPlusCircle.vue +0 -8
  150. package/lib/components/icons/SIconPlusOff.vue +0 -7
  151. package/lib/components/icons/SIconPreloaderDark.vue +0 -52
  152. package/lib/components/icons/SIconPreloaderLight.vue +0 -52
  153. package/lib/components/icons/SIconProgress.vue +0 -5
  154. package/lib/components/icons/SIconRadio.vue +0 -6
  155. package/lib/components/icons/SIconSave.vue +0 -5
  156. package/lib/components/icons/SIconSearch.vue +0 -5
  157. package/lib/components/icons/SIconSend.vue +0 -5
  158. package/lib/components/icons/SIconSettings.vue +0 -6
  159. package/lib/components/icons/SIconShare2.vue +0 -5
  160. package/lib/components/icons/SIconSkipBackFill.vue +0 -6
  161. package/lib/components/icons/SIconSliders.vue +0 -12
  162. package/lib/components/icons/SIconSun.vue +0 -13
  163. package/lib/components/icons/SIconTelescope.vue +0 -5
  164. package/lib/components/icons/SIconTrash.vue +0 -5
  165. package/lib/components/icons/SIconTrash2.vue +0 -7
  166. package/lib/components/icons/SIconTrash2Off.vue +0 -6
  167. package/lib/components/icons/SIconTrello.vue +0 -7
  168. package/lib/components/icons/SIconUser.vue +0 -6
  169. package/lib/components/icons/SIconUsers.vue +0 -8
  170. package/lib/components/icons/SIconWarning.vue +0 -7
  171. package/lib/components/icons/SIconX.vue +0 -5
  172. package/lib/components/icons/SIconXCircle.vue +0 -6
  173. package/lib/components/icons/SIconXCircleThin.vue +0 -6
  174. package/lib/components/icons/SIconXSquare.vue +0 -6
  175. package/lib/components/icons/SIconZap.vue +0 -5
  176. package/lib/composables/Dialog.ts +0 -38
  177. package/lib/composables/Modal.ts +0 -34
  178. package/lib/composables/Snackbar.ts +0 -18
  179. package/lib/store/Sefirot.ts +0 -17
  180. package/lib/store/dialog/index.ts +0 -42
  181. package/lib/store/modal/index.ts +0 -61
  182. package/lib/store/snackbars/index.ts +0 -70
@@ -1,146 +1,101 @@
1
- <template>
2
- <SInputBase
3
- class="SInputDropdown"
4
- :class="classes"
5
- :label="label"
6
- :note="note"
7
- :help="help"
8
- :validation="validation"
9
- >
10
- <div ref="container" class="SInputDropdown-container">
11
- <div
12
- class="SInputDropdown-box"
13
- role="button"
14
- tabindex="0"
15
- @click="handleOpen"
16
- @keydown.down.prevent
17
- @keyup.enter="handleOpen"
18
- @keyup.down="handleOpen"
19
- >
20
- <div class="SInputDropdown-box-content">
21
- <SInputDropdownItem
22
- v-if="hasSelected"
23
- :item="selected"
24
- :disabled="disabled"
25
- @remove="handleCallback"
26
- />
27
-
28
- <span v-else class="SInputDropdown-box-placeholder">{{ placeholder }}</span>
29
- </div>
30
-
31
- <div class="SInputDropdown-box-icon">
32
- <SIconChevronUp class="SInputDropdown-box-icon-svg up" />
33
- <SIconChevronDown class="SInputDropdown-box-icon-svg down" />
34
- </div>
35
- </div>
36
-
37
- <div v-if="isOpen" class="SInputDropdown-dropdown">
38
- <SDropdown :options="dropdownOptions" @close="close" />
39
- </div>
40
- </div>
41
- </SInputBase>
42
- </template>
43
-
44
1
  <script setup lang="ts">
45
- import { PropType, computed, nextTick } from 'vue'
46
- import { isNullish, isArray, isEqual } from '../support/Utils'
2
+ import IconCaretDown from '@iconify-icons/ph/caret-down'
3
+ import IconCaretUp from '@iconify-icons/ph/caret-up'
4
+ import xor from 'lodash-es/xor'
5
+ import { computed } from 'vue'
6
+ import type { DropdownSectionFilter } from '../composables/Dropdown'
47
7
  import { useFlyout } from '../composables/Flyout'
48
- import { Search, Item, UseDropdownSearchOptions, useDropdown } from '../composables/Dropdown'
49
- import SIconChevronUp from './icons/SIconChevronUp.vue'
50
- import SIconChevronDown from './icons/SIconChevronDown.vue'
8
+ import { Validatable } from '../composables/Validation'
9
+ import { isArray } from '../support/Utils'
51
10
  import SDropdown from './SDropdown.vue'
11
+ import SIcon from './SIcon.vue'
52
12
  import SInputBase from './SInputBase.vue'
53
13
  import SInputDropdownItem from './SInputDropdownItem.vue'
54
14
 
55
- type Size = 'mini' | 'small' | 'medium'
56
- type Value = string | number | boolean | unknown[]
57
-
58
- const props = defineProps({
59
- size: { type: String as PropType<Size>, default: 'small' },
60
- label: { type: String, default: null },
61
- note: { type: String, default: null },
62
- help: { type: String, default: null },
63
- placeholder: { type: String, default: null },
64
- search: { type: [Boolean, Object] as PropType<boolean | Search>, default: false },
65
- options: { type: Array as PropType<Item[]>, required: true },
66
- nullable: { type: Boolean, default: true },
67
- closeOnClick: { type: Boolean, default: false },
68
- disabled: { type: Boolean, default: false },
69
- modelValue: { type: [String, Number, Boolean, Array, Object] as PropType<Value>, default: null },
70
- validation: { type: Object as PropType<any>, default: null }
71
- })
15
+ export type Size = 'mini' | 'small' | 'medium'
16
+ export type PrimitiveValue = string | number | boolean | null
17
+ export type ArrayValue = (string | number | boolean)[]
18
+ export type OptionValue = string | number | boolean
19
+
20
+ export type Option = OptionText | OptionAvatar
72
21
 
73
- const emit = defineEmits(['update:modelValue'])
22
+ export interface OptionBase {
23
+ type?: 'text' | 'avatar'
24
+ value: OptionValue
25
+ }
74
26
 
75
- const { container, isOpen, open, close } = useFlyout()
27
+ export interface OptionText extends OptionBase {
28
+ type?: 'text'
29
+ label: string
30
+ }
31
+
32
+ export interface OptionAvatar extends OptionBase {
33
+ type: 'avatar'
34
+ label: string
35
+ image?: string | null
36
+ }
37
+
38
+ const props = defineProps<{
39
+ size?: Size
40
+ label?: string
41
+ note?: string
42
+ help?: string
43
+ placeholder?: string
44
+ noSearch?: boolean
45
+ nullable?: boolean
46
+ closeOnClick?: boolean
47
+ options: Option[]
48
+ disabled?: boolean
49
+ modelValue: PrimitiveValue | ArrayValue
50
+ validation?: Validatable
51
+ }>()
52
+
53
+ const emit = defineEmits<{
54
+ (e: 'update:modelValue', value: PrimitiveValue | ArrayValue): void
55
+ }>()
56
+
57
+ const { container, isOpen, open } = useFlyout()
76
58
 
77
59
  const classes = computed(() => [
78
- props.size,
60
+ props.size ?? 'small',
79
61
  { disabled: props.disabled }
80
62
  ])
81
63
 
82
- const enabledItems = computed(() => {
83
- return props.options.filter(option => !option.disabled)
84
- })
85
-
86
- const dropdownOptions = useDropdown({
87
- search: createDropdownSearchOptions(),
88
- items: enabledItems,
89
- closeOnClick: props.closeOnClick,
90
- selected: computed(() => props.modelValue),
91
- callback: handleCallback
92
- })
64
+ const dropdownOptions = computed<DropdownSectionFilter[]>(() => [{
65
+ type: 'filter',
66
+ search: props.noSearch === undefined ? true : !props.noSearch,
67
+ selected: props.modelValue,
68
+ options: props.options,
69
+ onClick: handleSelect
70
+ }])
93
71
 
94
72
  const selected = computed(() => {
95
- return isArray(props.modelValue)
96
- ? props.options.filter(o => (props.modelValue as unknown[]).includes(o.value))
97
- : props.options.find(o => isEqual(o.value, props.modelValue))
98
- })
99
-
100
- const hasSelected = computed(() => {
101
- return isArray(selected.value)
102
- ? selected.value.length > 0
103
- : !isNullish(selected.value) && selected.value.value !== ''
104
- })
105
-
106
- function createDropdownSearchOptions(): UseDropdownSearchOptions | undefined {
107
- if (props.search === false) {
108
- return undefined
109
- }
110
-
111
- if (props.search === true) {
112
- return {
113
- placeholder: 'Search items',
114
- missing: 'No items found.'
115
- }
73
+ if (isArray(props.modelValue)) {
74
+ return props.options.filter((o) => (props.modelValue as ArrayValue).includes(o.value))
116
75
  }
117
76
 
118
- return props.search
119
- }
120
-
121
- async function handleOpen(): Promise<void> {
122
- if (!props.disabled) {
123
- open()
77
+ const item = props.options.find((o) => o.value === props.modelValue)
124
78
 
125
- await nextTick()
79
+ return item ? [item] : []
80
+ })
126
81
 
127
- const el = document.querySelector<HTMLInputElement>('.SInputDropdown .SDropdown .search .SInputText input')
82
+ const hasSelected = computed(() => {
83
+ return selected.value.length > 0
84
+ })
128
85
 
129
- el && el.focus()
130
- }
86
+ async function handleOpen() {
87
+ !props.disabled && open()
131
88
  }
132
89
 
133
- function handleCallback(item: Item): void {
134
- props.validation && props.validation.$touch()
90
+ function handleSelect(value: OptionValue) {
91
+ props.validation?.$touch()
135
92
 
136
- isArray(props.modelValue) ? handleArray(item.value) : handlePrimitive(item.value)
93
+ isArray(props.modelValue) ? handleArray(value) : handlePrimitive(value)
137
94
  }
138
95
 
139
- function handlePrimitive(value: unknown): void {
140
- if (!isEqual(props.modelValue, value)) {
141
- emit('update:modelValue', value)
142
-
143
- return
96
+ function handlePrimitive(value: OptionValue) {
97
+ if (value !== props.modelValue) {
98
+ return emit('update:modelValue', value)
144
99
  }
145
100
 
146
101
  if (props.nullable) {
@@ -148,8 +103,8 @@ function handlePrimitive(value: unknown): void {
148
103
  }
149
104
  }
150
105
 
151
- function handleArray(value: unknown[]): void {
152
- const difference = getDifference(props.modelValue as unknown[], value)
106
+ function handleArray(value: OptionValue) {
107
+ const difference = xor(props.modelValue as ArrayValue, [value])
153
108
 
154
109
  if (!props.nullable && difference.length === 0) {
155
110
  return
@@ -157,76 +112,113 @@ function handleArray(value: unknown[]): void {
157
112
 
158
113
  emit('update:modelValue', difference)
159
114
  }
160
-
161
- function getDifference(source: unknown[], value: unknown[]): unknown[] {
162
- return source
163
- .filter(item => !isEqual(item, value))
164
- .concat(source.includes(value) ? [] : [value])
165
- }
166
115
  </script>
167
116
 
117
+ <template>
118
+ <SInputBase
119
+ class="SInputDropdown"
120
+ :class="classes"
121
+ :label="label"
122
+ :note="note"
123
+ :help="help"
124
+ :validation="validation"
125
+ >
126
+ <div class="container" ref="container">
127
+ <div
128
+ class="box"
129
+ role="button"
130
+ tabindex="0"
131
+ @click="handleOpen"
132
+ @keydown.down.prevent
133
+ @keyup.enter="handleOpen"
134
+ @keyup.down="handleOpen"
135
+ >
136
+ <div class="box-content">
137
+ <SInputDropdownItem
138
+ v-if="hasSelected"
139
+ :items="selected"
140
+ :disabled="disabled ?? false"
141
+ @remove="handleSelect"
142
+ />
143
+
144
+ <div v-else class="box-placeholder">{{ placeholder }}</div>
145
+ </div>
146
+
147
+ <div class="box-icon">
148
+ <SIcon :icon="IconCaretUp" class="box-icon-svg up" />
149
+ <SIcon :icon="IconCaretDown" class="box-icon-svg down" />
150
+ </div>
151
+ </div>
152
+
153
+ <div v-if="isOpen" class="dropdown">
154
+ <SDropdown :sections="dropdownOptions" />
155
+ </div>
156
+ </div>
157
+ </SInputBase>
158
+ </template>
159
+
168
160
  <style lang="postcss" scoped>
169
161
  .SInputDropdown.mini {
170
- .SInputDropdown-box {
162
+ .box {
171
163
  min-height: 32px;
172
164
  }
173
165
 
174
- .SInputDropdown-box-content {
166
+ .box-content {
175
167
  padding: 3px 30px 3px 12px;
176
168
  line-height: 24px;
177
169
  font-size: 14px;
178
170
  }
179
171
 
180
- .SInputDropdown-box-icon {
172
+ .box-icon {
181
173
  top: 3px;
182
174
  right: 8px;
183
175
  }
184
176
  }
185
177
 
186
178
  .SInputDropdown.small {
187
- .SInputDropdown-box {
179
+ .box {
188
180
  min-height: 40px;
189
181
  }
190
182
 
191
- .SInputDropdown-box-content {
192
- padding: 7px 30px 7px 12px;
183
+ .box-content {
184
+ padding: 5px 30px 5px 8px;
193
185
  line-height: 24px;
194
- font-size: 14px;
186
+ font-size: 16px;
195
187
  }
196
188
 
197
- .SInputDropdown-box-icon {
189
+ .box-icon {
198
190
  top: 7px;
199
191
  right: 8px;
200
192
  }
201
193
  }
202
194
 
203
195
  .SInputDropdown.medium {
204
- .SInputDropdown-box {
196
+ .box {
205
197
  height: 48px;
206
198
  }
207
199
 
208
- .SInputDropdown-box-content {
200
+ .box-content {
209
201
  padding: 11px 44px 11px 16px;
210
202
  line-height: 24px;
211
203
  font-size: 16px;
212
204
  }
213
205
 
214
- .SInputDropdown-box-icon {
206
+ .box-icon {
215
207
  top: 11px;
216
208
  right: 12px;
217
209
  }
218
210
  }
219
211
 
220
212
  .SInputDropdown.disabled {
221
- .SInputDropdown-box {
222
- background-color: var(--input-disabled-bg);
213
+ .box {
214
+ background-color: var(--c-bg);
223
215
  cursor: not-allowed;
224
216
 
225
- &:hover { border-color: var(--input-outlined-border); }
226
- &:focus:not(:focus-visible) { border-color: var(--input-outlined-border); }
217
+ &:hover { border-color: var(--c-divider); }
218
+ &:focus:not(:focus-visible) { border-color: var(--c-divider); }
227
219
  }
228
220
 
229
- .SInputDropdown-box-icon {
221
+ .box-icon {
230
222
  cursor: not-allowed;
231
223
  }
232
224
  }
@@ -237,53 +229,63 @@ function getDifference(source: unknown[], value: unknown[]): unknown[] {
237
229
  }
238
230
  }
239
231
 
240
- .SInputDropdown-container {
232
+ .container {
241
233
  position: relative;
242
234
  }
243
235
 
244
- .SInputDropdown-box {
236
+ .box {
245
237
  position: relative;
246
- border: 1px solid var(--input-border);
247
- border-radius: 4px;
238
+ border: 1px solid var(--c-divider);
239
+ border-radius: 6px;
248
240
  width: 100%;
249
241
  color: var(--input-text);
242
+ background-color: var(--c-bg);
250
243
  cursor: pointer;
251
244
  transition: border-color .25s, background-color .25s;
252
245
 
253
246
  &:hover {
254
- border-color: var(--input-focus-border);
247
+ border-color: var(--c-black);
255
248
  }
256
249
 
257
- &:focus {
258
- border-color: var(--input-focus-border);
259
- outline: 0;
250
+ &:focus,
251
+ &:hover.focus {
252
+ border-color: var(--c-info);
260
253
  }
261
254
 
255
+ .dark &:hover {
256
+ border-color: var(--c-gray);
257
+ }
258
+
259
+ .dark &:focus,
260
+ .dark &:hover:focus {
261
+ border-color: var(--c-info);
262
+ }
262
263
  }
263
264
 
264
- .SInputDropdown-box-placeholder {
265
+ .box-placeholder {
266
+ padding: 2px 4px;
265
267
  font-weight: 500;
266
- color: var(--input-placeholder);
268
+ color: var(--c-text-3);
267
269
  }
268
270
 
269
- .SInputDropdown-box-icon {
271
+ .box-icon {
270
272
  position: absolute;
271
273
  z-index: 10;
272
274
  cursor: pointer;
273
275
  }
274
276
 
275
- .SInputDropdown-box-icon-svg {
277
+ .box-icon-svg {
276
278
  display: block;
277
279
  width: 14px;
278
280
  height: 14px;
279
- fill: var(--input-placeholder);
281
+ color: var(--c-text-2);
280
282
  }
281
283
 
282
- .SInputDropdown-box-icon-svg.up {
284
+ .box-icon-svg.up {
283
285
  margin-bottom: -4px;
284
286
  }
285
287
 
286
- .SInputDropdown-dropdown {
288
+ .dropdown {
287
289
  position: absolute;
288
290
  top: calc(100% + 8px);
289
291
  left: 0;
@@ -1,63 +1,61 @@
1
- <template>
2
- <div class="SInputDropdownItem">
3
- <div v-for="(i, index) in arrayItem" :key="index" class="item">
4
- <component
5
- :is="component(i)"
6
- :mute="shouldMute(i)"
7
- :item="i"
8
- :disabled="disabled"
9
- @remove="$emit('remove', i)"
10
- />
11
- </div>
12
- </div>
13
- </template>
14
-
15
1
  <script setup lang="ts">
16
- import { PropType, computed } from 'vue'
17
- import { isArray } from '../support/Utils'
2
+ import SInputDropdownItemAvatar from './SInputDropdownItemAvatar.vue'
18
3
  import SInputDropdownItemText from './SInputDropdownItemText.vue'
19
- import SInputDropdownItemTextTag from './SInputDropdownItemTextTag.vue'
20
- import SInputDropdownItemUser from './SInputDropdownItemUser.vue'
21
- import SInputDropdownItemUserTag from './SInputDropdownItemUserTag.vue'
22
-
23
- const props = defineProps({
24
- item: { type: [Object, Array] as PropType<any>, default: null },
25
- disabled: { type: Boolean, default: false }
26
- })
27
-
28
- defineEmits(['remove'])
29
-
30
- const isItemArray = computed(() => isArray(props.item))
31
4
 
32
- const arrayItem = computed(() => {
33
- return isArray(props.item) ? props.item : [props.item]
34
- })
5
+ export type Item = ItemText | ItemAvatar
35
6
 
36
- function component(item: any): any {
37
- if (item.type === 'text') {
38
- return isItemArray.value ? SInputDropdownItemTextTag : SInputDropdownItemText
39
- }
40
-
41
- if (item.type === 'user') {
42
- return isItemArray.value ? SInputDropdownItemUserTag : SInputDropdownItemUser
43
- }
7
+ export interface ItemBase {
8
+ type?: 'text' | 'avatar'
9
+ value: string | number | boolean
10
+ }
44
11
 
45
- throw new Error('Invalid item type.')
12
+ export interface ItemText extends ItemBase {
13
+ type?: 'text'
14
+ label: string
46
15
  }
47
16
 
48
- function shouldMute(item: any) {
49
- return item.value === null
17
+ export interface ItemAvatar extends ItemBase {
18
+ type: 'avatar'
19
+ label: string
20
+ image?: string | null
50
21
  }
22
+
23
+ defineProps<{
24
+ items: Item[]
25
+ disabled: boolean
26
+ }>()
27
+
28
+ defineEmits<{
29
+ (e: 'remove', value: string | number | boolean): void
30
+ }>()
51
31
  </script>
52
32
 
53
- <style lang="postcss" scoped>
33
+ <template>
34
+ <div class="SInputDropdownItem">
35
+ <div v-for="(item, index) in items" :key="index" class="item">
36
+ <SInputDropdownItemText
37
+ v-if="item.type === 'text' || item.type === undefined"
38
+ :label="item.label"
39
+ :value="item.value"
40
+ :disabled="disabled"
41
+ @remove="(v) => $emit('remove', v)"
42
+ />
43
+ <SInputDropdownItemAvatar
44
+ v-if="item.type === 'avatar'"
45
+ :label="item.label"
46
+ :image="item.image"
47
+ :value="item.value"
48
+ :disabled="disabled"
49
+ @remove="(v) => $emit('remove', v)"
50
+ />
51
+ </div>
52
+ </div>
53
+ </template>
54
+
55
+ <style scoped lang="postcss">
54
56
  .SInputDropdownItem {
55
57
  display: flex;
56
58
  flex-wrap: wrap;
57
- margin: -2px -8px;
58
- }
59
-
60
- .item {
61
- padding: 2px;
59
+ gap: 4px;
62
60
  }
63
61
  </style>
@@ -0,0 +1,99 @@
1
+ <script setup lang="ts">
2
+ import IconX from '@iconify-icons/ph/x'
3
+ import SAvatar from './SAvatar.vue'
4
+ import SIcon from './SIcon.vue'
5
+
6
+ defineProps<{
7
+ label: string
8
+ image?: string | null
9
+ value: string | number | boolean
10
+ disabled: boolean
11
+ }>()
12
+
13
+ defineEmits<{
14
+ (e: 'remove', value: string | number | boolean): void
15
+ }>()
16
+ </script>
17
+
18
+ <template>
19
+ <div class="SInputDropdownItemAvatar" :class="{ disabled }">
20
+ <div class="user">
21
+ <div class="avatar">
22
+ <SAvatar size="nano" :avatar="image" :name="label" />
23
+ </div>
24
+ <p class="name">{{ label }}</p>
25
+ </div>
26
+
27
+ <div v-if="!disabled" class="remove" role="button" @click="$emit('remove', value)">
28
+ <div class="remove-box">
29
+ <SIcon :icon="IconX" class="remove-icon" />
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <style lang="postcss" scoped>
36
+ .SInputDropdownItemAvatar {
37
+ display: flex;
38
+ border: 1px solid var(--c-divider-light);
39
+ border-radius: 14px;
40
+ padding: 0;
41
+ background-color: var(--c-bg-mute);
42
+ }
43
+
44
+ .SInputDropdownItemUserAvatar.disabled {
45
+ padding: 0 10px 0 0;
46
+ background-color: var(--c-gray-light-4);
47
+ }
48
+
49
+ .user {
50
+ display: flex;
51
+ }
52
+
53
+ .avatar {
54
+ padding: 3px 0 0 3px;
55
+ }
56
+
57
+ .name {
58
+ margin: 0 0 0 8px;
59
+ line-height: 26px;
60
+ font-size: 12px;
61
+ font-weight: 500;
62
+ white-space: nowrap;
63
+ }
64
+
65
+ .remove {
66
+ display: flex;
67
+ justify-content: center;
68
+ align-items: center;
69
+ margin-left: 2px;
70
+ width: 26px;
71
+ height: 26px;
72
+ }
73
+
74
+ .remove-box {
75
+ display: flex;
76
+ justify-content: center;
77
+ align-items: center;
78
+ border-radius: 50%;
79
+ width: 20px;
80
+ height: 20px;
81
+ color: var(--c-text-2);
82
+ transition: color 0.25s, background-color 0.25s;
83
+
84
+ .remove:hover & {
85
+ color: var(--c-text-1);
86
+ background-color: var(--c-gray-light-3)
87
+ }
88
+
89
+ .dark .remove:hover & {
90
+ color: var(--c-text-1);
91
+ background-color: var(--c-gray-dark-3)
92
+ }
93
+ }
94
+
95
+ .remove-icon {
96
+ width: 12px;
97
+ height: 12px;
98
+ }
99
+ </style>