@globalbrain/sefirot 2.0.0-draft.7 → 2.0.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 (95) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +6 -6
  3. package/lib/components/SAvatar.vue +17 -17
  4. package/lib/components/SButton.vue +512 -267
  5. package/lib/components/SButtonGroup.vue +149 -0
  6. package/lib/components/SDropdown.vue +26 -150
  7. package/lib/components/SDropdownSection.vue +48 -0
  8. package/lib/components/SDropdownSectionFilter.vue +189 -0
  9. package/lib/components/SDropdownSectionFilterItem.vue +21 -0
  10. package/lib/components/SDropdownSectionFilterItemAvatar.vue +31 -0
  11. package/lib/components/SDropdownSectionFilterItemText.vue +20 -0
  12. package/lib/components/SDropdownSectionMenu.vue +39 -0
  13. package/lib/components/SIcon.vue +13 -0
  14. package/lib/components/SInputBase.vue +31 -31
  15. package/lib/components/SInputCheckbox.vue +1 -1
  16. package/lib/components/SInputCheckboxes.vue +74 -0
  17. package/lib/components/SInputDate.vue +182 -0
  18. package/lib/components/SInputDropdown.vue +158 -157
  19. package/lib/components/SInputDropdownItem.vue +46 -48
  20. package/lib/components/{SInputDropdownItemUserTag.vue → SInputDropdownItemAvatar.vue} +43 -44
  21. package/lib/components/SInputDropdownItemText.vue +79 -16
  22. package/lib/components/SInputFile.vue +55 -60
  23. package/lib/components/SInputHMS.vue +120 -110
  24. package/lib/components/SInputNumber.vue +38 -9
  25. package/lib/components/SInputRadio.vue +39 -36
  26. package/lib/components/SInputRadios.vue +40 -53
  27. package/lib/components/SInputSelect.vue +3 -3
  28. package/lib/components/SInputSwitch.vue +193 -0
  29. package/lib/components/SInputSwitches.vue +88 -0
  30. package/lib/components/SInputText.vue +206 -62
  31. package/lib/components/SInputTextarea.vue +46 -32
  32. package/lib/components/SInputYMD.vue +123 -126
  33. package/lib/components/SMarkdown.vue +52 -0
  34. package/lib/components/SModal.vue +25 -63
  35. package/lib/components/SMount.vue +19 -0
  36. package/lib/components/SSheet.vue +49 -55
  37. package/lib/components/SSheetFooter.vue +1 -1
  38. package/lib/components/SSheetFooterAction.vue +24 -17
  39. package/lib/components/SSheetFooterActions.vue +1 -4
  40. package/lib/components/SSheetForm.vue +15 -0
  41. package/lib/components/SSheetMedium.vue +8 -10
  42. package/lib/components/SSheetTitle.vue +7 -14
  43. package/lib/components/SSnackbar.vue +55 -45
  44. package/lib/components/{SPortalSnackbars.vue → SSnackbars.vue} +17 -20
  45. package/lib/components/SStep.vue +106 -0
  46. package/lib/components/SSteps.vue +59 -0
  47. package/lib/components/STable.vue +241 -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 +102 -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 +6 -7
  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 +62 -43
  81. package/CHANGELOG.md +0 -41
  82. package/lib/components/SDialog.vue +0 -140
  83. package/lib/components/SDropdownItem.vue +0 -78
  84. package/lib/components/SDropdownItemText.vue +0 -22
  85. package/lib/components/SDropdownItemUser.vue +0 -40
  86. package/lib/components/SInputDropdownItemTextTag.vue +0 -94
  87. package/lib/components/SInputDropdownItemUser.vue +0 -41
  88. package/lib/components/SPortalModals.vue +0 -74
  89. package/lib/composables/Dialog.ts +0 -38
  90. package/lib/composables/Modal.ts +0 -34
  91. package/lib/composables/Snackbar.ts +0 -18
  92. package/lib/store/Sefirot.ts +0 -17
  93. package/lib/store/dialog/index.ts +0 -42
  94. package/lib/store/modal/index.ts +0 -61
  95. package/lib/store/snackbars/index.ts +0 -70
@@ -0,0 +1,149 @@
1
+ <template>
2
+ <div class="SButtonGroup" :class="classes">
3
+ <button
4
+ v-for="item in items"
5
+ :key="item.value"
6
+ class="button"
7
+ :class="getButtonClasses(item)"
8
+ @click="handleClick(item.value)"
9
+ >
10
+ <span class="content">
11
+ {{ item.label }}
12
+ </span>
13
+ </button>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { computed, PropType } from 'vue'
19
+
20
+ interface ButtonGroupItem {
21
+ label: string
22
+ value: string
23
+ mode: Mode
24
+ }
25
+
26
+ type Mode = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
27
+ type Size = 'mini' | 'small' | 'medium' | 'large' | 'jumbo'
28
+
29
+
30
+ const props = defineProps({
31
+ items: { type: Array as PropType<ButtonGroupItem[]>, required: true },
32
+ size: { type: String as PropType<Size>, default: 'medium' },
33
+ modelValue: { type: String, default: null }
34
+ })
35
+
36
+ const emit = defineEmits(['update:modelValue'])
37
+
38
+ const classes = computed(() => [props.size])
39
+
40
+ function getButtonClasses(button: ButtonGroupItem) {
41
+ return [
42
+ { active: button.value === props.modelValue },
43
+ button.mode ?? 'neutral'
44
+ ]
45
+ }
46
+
47
+ function handleClick(value: string) {
48
+ emit('update:modelValue', value)
49
+ }
50
+ </script>
51
+
52
+ <style lang="postcss" scoped>
53
+ .SButtonGroup {
54
+ display: flex;
55
+ width: fit-content;
56
+ border: 1px solid var(--c-divider);
57
+ border-radius: 4px;
58
+ overflow: hidden;
59
+ }
60
+
61
+ .SButtonGroup.mini {
62
+ height: 28px;
63
+
64
+ .button {
65
+ padding: 0 8px;
66
+ height: 28px;
67
+ font-size: 12px;
68
+ font-weight: 500;
69
+ }
70
+ }
71
+
72
+ .SButtonGroup.small {
73
+ height: 32px;
74
+
75
+ .button {
76
+ padding: 0 10px;
77
+ height: 32px;
78
+ font-size: 12px;
79
+ font-weight: 500;
80
+ }
81
+ }
82
+
83
+ .SButtonGroup.medium {
84
+ height: 40px;
85
+
86
+ .button {
87
+ padding: 0 12px;
88
+ height: 40px;
89
+ font-size: 13px;
90
+ font-weight: 500;
91
+ }
92
+ }
93
+
94
+ .SButtonGroup.large {
95
+ height: 48px;
96
+
97
+ .button {
98
+ padding: 0 14px;
99
+ height: 48px;
100
+ font-size: 14px;
101
+ font-weight: 500;
102
+ }
103
+ }
104
+
105
+ .SButtonGroup.jumbo {
106
+ height: 64px;
107
+
108
+ .button {
109
+ padding: 0 24px;
110
+ height: 64px;
111
+ font-size: 14px;
112
+ font-weight: 500;
113
+ }
114
+ }
115
+
116
+ .button {
117
+ border-left: 1px solid transparent;
118
+ letter-spacing: .4px;
119
+ color: var(--c-text-2);
120
+ white-space: nowrap;
121
+ transition: color .25s, background-color .25s;
122
+
123
+ &:hover {
124
+ color: var(--c-text-1);
125
+ }
126
+ }
127
+
128
+ .button:not(:first-child) {
129
+ border-left: 1px solid var(--c-divider);
130
+ }
131
+
132
+ .button.active {
133
+ color: var(--c-text-dark-1);
134
+ }
135
+
136
+ .button.neutral.active { background-color: var(--c-black); }
137
+ .button.info.active { background-color: var(--c-info); }
138
+ .button.success.active { background-color: var(--c-success); }
139
+ .button.warning.active { background-color: var(--c-warning); }
140
+ .button.danger.active { background-color: var(--c-danger); }
141
+
142
+ .content {
143
+ display: flex;
144
+ justify-content: center;
145
+ align-items: center;
146
+ width: 100%;
147
+ height: 100%;
148
+ }
149
+ </style>
@@ -1,167 +1,43 @@
1
- <template>
2
- <div class="SDropdown">
3
- <div v-if="options.title" class="header">
4
- <p class="title">{{ options.title }}</p>
5
- <button class="close" @click="$emit('close')">
6
- <SIconX class="close-icon" />
7
- </button>
8
- </div>
1
+ <script setup lang="ts">
2
+ import { DropdownSection } from '../composables/Dropdown'
3
+ import SDropdownSection from './SDropdownSection.vue'
9
4
 
10
- <div v-if="options.search" class="search">
11
- <SInputText
12
- size="mini"
13
- mode="outlined"
14
- :placeholder="options.search.placeholder"
15
- :model-value="options.search.value.value"
16
- @update:model-value="v => options.search?.onInput(v)"
17
- />
18
- </div>
5
+ defineProps<{
6
+ sections: DropdownSection[]
7
+ }>()
8
+ </script>
19
9
 
10
+ <template>
11
+ <div class="SDropdown">
20
12
  <div class="container">
21
- <ul v-if="options.items.value.length > 0" class="list">
22
- <li
23
- v-for="(item, index) in options.items.value"
24
- :key="index"
25
- class="item"
26
- tabindex="0"
27
- @keydown.up.prevent
28
- @keydown.down.prevent
29
- @keyup.up.prevent="focusPrev"
30
- @keyup.down.prevent="focusNext"
31
- @keyup.enter="onClick(item)"
32
- @keyup.escape="$emit('close')"
33
- @click="onClick(item)"
34
- >
35
- <SDropdownItem :selected="options.selected" :item="item" />
36
- </li>
37
- </ul>
38
-
39
- <p v-else class="item-missing">{{ options.search?.missing }}</p>
13
+ <div v-for="(section, index) in sections" :key="index" class="section">
14
+ <SDropdownSection :section="section" />
15
+ </div>
40
16
  </div>
41
17
  </div>
42
18
  </template>
43
19
 
44
- <script setup lang="ts">
45
- import { PropType } from 'vue'
46
- import { Dropdown, Item } from '../composables/Dropdown'
47
- import SIconX from './icons/SIconX.vue'
48
- import SInputText from './SInputText.vue'
49
- import SDropdownItem from './SDropdownItem.vue'
50
-
51
- const props = defineProps({
52
- round: { type: Number, default: 8 },
53
- options: { type: Object as PropType<Dropdown>, required: true }
54
- })
55
-
56
- const emit = defineEmits(['close'])
57
-
58
- function focusFirstItem(): void {
59
- const el = document.querySelector('.SDropdown .item:first-child') as HTMLElement | null
60
- el?.focus?.()
61
- }
62
-
63
- function focusPrev(event: any): void {
64
- event.target.previousSibling?.focus()
65
- }
66
-
67
- function focusNext(event: any): void {
68
- event.target.nextSibling?.focus()
69
- }
70
-
71
- function onClick(item: Item): void {
72
- if (item.callback) {
73
- item.callback()
74
- } else if (props.options.callback) {
75
- props.options.callback(item)
76
- }
77
-
78
- if (props.options.closeOnClick) {
79
- emit('close')
80
- }
81
- }
82
- </script>
83
-
84
- <style lang="postcss" scoped>
20
+ <style scoped lang="postcss">
85
21
  .SDropdown {
86
- display: inline-block;
87
- overflow: hidden;
88
- min-width: 192px;
89
22
  border: 1px solid var(--c-divider-light);
90
- border-radius: 8px;
91
- background-color: var(--c-white);
92
- box-shadow: var(--shadow-depth-3);
93
- transition: background-color .25s;
94
- }
95
-
96
- .dark .SDropdown {
97
- background-color: var(--c-black-soft);
98
- }
99
-
100
- .header {
101
- display: flex;
102
- justify-content: space-between;
103
- border-bottom: 1px solid var(--c-divider-light);
104
- padding: 0 0 0 16px;
105
- }
106
-
107
- .title {
108
- padding: 4px 0 3px;
109
- font-size: 12px;
110
- font-weight: 500;
111
- color: var(--c-text-2);
112
- }
113
-
114
- .close {
115
- display: flex;
116
- justify-content: center;
117
- align-items: center;
118
- width: 32px;
119
- height: 31px;
120
- color: var(--c-text-2);
121
-
122
- &:hover {
123
- color: var(--c-text-1);
124
- }
125
- }
126
-
127
- .close-icon {
128
- width: 14px;
129
- height: 14px;
130
- fill: currentColor;
131
- }
132
-
133
- .search {
134
- padding: 8px 8px 7px;
135
- border-bottom: 1px solid var(--c-divider-light);
136
- }
137
-
138
- .container {
139
- max-height: 304px;
23
+ border-radius: 12px;
24
+ min-width: 256px;
25
+ max-height: 384px;
26
+ background-color: var(--c-bg);
140
27
  overflow-y: auto;
141
- }
142
28
 
143
- .item {
144
- &:hover,
145
- &:focus {
146
- background-color: var(--dropdown-item-hover-bg);
147
- outline: none;
29
+ &::-webkit-scrollbar {
30
+ display: none;
148
31
  }
149
- }
150
-
151
- .item:first-child {
152
- padding-top: 1px;
153
- }
154
32
 
155
- .item + .item {
156
- border-top: 1px solid var(--c-divider-light);
33
+ .dark & {
34
+ background-color: var(--c-bg-mute);
35
+ }
157
36
  }
158
37
 
159
- .item-missing {
160
- margin: 0;
161
- padding: 8px 16px;
162
- line-height: 16px;
163
- font-size: 12px;
164
- font-weight: 500;
165
- color: var(--c-text-2);
38
+ .section {
39
+ & + & {
40
+ border-top: 1px solid var(--c-divider-light);
41
+ }
166
42
  }
167
43
  </style>
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import { DropdownSection } from '../composables/Dropdown'
3
+ import SDropdownSectionFilter from './SDropdownSectionFilter.vue'
4
+ import SDropdownSectionMenu from './SDropdownSectionMenu.vue'
5
+
6
+ defineProps<{
7
+ section: DropdownSection
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <SDropdownSectionMenu
13
+ v-if="section.type === 'menu'"
14
+ :options="section.options"
15
+ />
16
+ <SDropdownSectionFilter
17
+ v-else-if="section.type === 'filter'"
18
+ :search="section.search"
19
+ :selected="section.selected"
20
+ :options="section.options"
21
+ :on-click="section.onClick"
22
+ />
23
+ </template>
24
+
25
+ <style scoped lang="postcss">
26
+ .SDropdown {
27
+ border: 1px solid var(--c-divider-light);
28
+ border-radius: 12px;
29
+ min-width: 256px;
30
+ max-height: 384px;
31
+ background-color: var(--c-bg);
32
+ overflow-y: auto;
33
+
34
+ &::-webkit-scrollbar {
35
+ display: none;
36
+ }
37
+
38
+ .dark & {
39
+ background-color: var(--c-bg-mute);
40
+ }
41
+ }
42
+
43
+ .section {
44
+ & + & {
45
+ border-top: 1px solid var(--c-divider-light);
46
+ }
47
+ }
48
+ </style>
@@ -0,0 +1,189 @@
1
+ <script setup lang="ts">
2
+ import type { MaybeRef } from '@vueuse/core'
3
+ import Fuse from 'fuse.js'
4
+ import { ref, computed, unref, onMounted } from 'vue'
5
+ import { DropdownSectionFilterSelectedValue, DropdownSectionFilterOption } from '../composables/Dropdown'
6
+ import { isArray } from '../support/Utils'
7
+ import SDropdownSectionFilterItem from './SDropdownSectionFilterItem.vue'
8
+ import SIconCheck from './icons/SIconCheck.vue'
9
+
10
+ const props = defineProps<{
11
+ search?: boolean
12
+ selected: MaybeRef<DropdownSectionFilterSelectedValue>
13
+ options: DropdownSectionFilterOption[]
14
+ onClick?(value: string | number | boolean): void
15
+ }>()
16
+
17
+ const input = ref<HTMLElement | null>(null)
18
+ const query = ref('')
19
+
20
+ const fuse = computed(() => {
21
+ return new Fuse(props.options, { keys: ['label'] })
22
+ })
23
+
24
+ const filteredOptions = computed(() => {
25
+ return !props.search || !query.value
26
+ ? props.options
27
+ : fuse.value.search(query.value).map((r) => r.item)
28
+ })
29
+
30
+ onMounted(() => {
31
+ input.value?.focus()
32
+ })
33
+
34
+ function isActive(value: string | number | boolean) {
35
+ const selected = unref(props.selected)
36
+
37
+ return isArray(selected)
38
+ ? selected.some((sv) => sv === value)
39
+ : selected === value
40
+ }
41
+
42
+ function focusPrev(event: any) {
43
+ event.target.parentNode.previousElementSibling?.firstElementChild?.focus()
44
+ }
45
+
46
+ function focusNext(event: any) {
47
+ event.target.parentNode.nextElementSibling?.firstElementChild?.focus()
48
+ }
49
+
50
+ function handleClick(option: DropdownSectionFilterOption, value: string | number | boolean) {
51
+ option.onClick && option.onClick(value)
52
+ props.onClick && props.onClick(value)
53
+ }
54
+ </script>
55
+
56
+ <template>
57
+ <div class="SDropdownSectionFilter">
58
+ <div v-if="search" class="search">
59
+ <input class="input" placeholder="Filter options" ref="input" v-model="query">
60
+ </div>
61
+
62
+ <ul v-if="filteredOptions.length" class="list">
63
+ <li v-for="option in filteredOptions" :key="option.label" class="item">
64
+ <button
65
+ class="button"
66
+ :class="{ active: isActive(option.value) }"
67
+ tabindex="0"
68
+ @keyup.up.prevent="focusPrev"
69
+ @keyup.down.prevent="focusNext"
70
+ @click="handleClick(option, option.value)"
71
+ >
72
+ <span class="checkbox">
73
+ <span class="checkbox-box">
74
+ <SIconCheck class="checkbox-icon" />
75
+ </span>
76
+ </span>
77
+ <span class="option-item">
78
+ <SDropdownSectionFilterItem :option="option" />
79
+ </span>
80
+ </button>
81
+ </li>
82
+ </ul>
83
+
84
+ <p v-else class="empty">
85
+ No options found.
86
+ </p>
87
+ </div>
88
+ </template>
89
+
90
+ <style scoped lang="postcss">
91
+ .search {
92
+ position: sticky;
93
+ top: 0;
94
+ border-bottom: 1px solid var(--c-divider-light);
95
+ padding: 8px;
96
+ background-color: var(--c-bg-elv-up);
97
+ }
98
+
99
+ .input {
100
+ border: 1px solid var(--c-divider);
101
+ border-radius: 6px;
102
+ padding: 0 8px;
103
+ width: 100%;
104
+ font-size: 12px;
105
+ line-height: 32px;
106
+ background-color: var(--c-bg);
107
+ transition: border-color 0.25s;
108
+
109
+ &::placeholder {
110
+ font-weight: 500;
111
+ color: var(--c-text-3);
112
+ }
113
+
114
+ &:hover { border-color: var(--c-black); }
115
+ &:focus { border-color: var(--c-info); }
116
+
117
+ .dark &:hover { border-color: var(--c-gray); }
118
+ .dark &:focus { border-color: var(--c-info); }
119
+ }
120
+
121
+ .list {
122
+ padding: 8px;
123
+ }
124
+
125
+ .button {
126
+ display: flex;
127
+ align-items: center;
128
+ border-radius: 6px;
129
+ padding: 0 8px;
130
+ width: 100%;
131
+ text-align: left;
132
+ transition: color 0.25s, background-color 0.25s;
133
+
134
+ &:hover,
135
+ &:focus {
136
+ background-color: var(--c-bg-mute);
137
+ }
138
+
139
+ .dark &:hover,
140
+ .dark &:focus {
141
+ background-color: var(--c-bg);
142
+ }
143
+ }
144
+
145
+ .checkbox {
146
+ display: block;
147
+ }
148
+
149
+ .checkbox-box {
150
+ display: flex;
151
+ justify-content: center;
152
+ align-items: center;
153
+ border: 1px solid var(--c-divider);
154
+ border-radius: 4px;
155
+ width: 16px;
156
+ height: 16px;
157
+ background-color: var(--c-bg);
158
+ transition: border-color 0.1s, background-color 0.1s;
159
+
160
+ .button.active & {
161
+ border-color: var(--c-info);
162
+ background-color: var(--c-info);
163
+ }
164
+ }
165
+
166
+ .checkbox-icon {
167
+ display: block;
168
+ width: 10px;
169
+ height: 10px;
170
+ fill: var(--c-white);
171
+ opacity: 0;
172
+ transition: opacity 0.25s;
173
+
174
+ .button.active & {
175
+ opacity: 1;
176
+ }
177
+ }
178
+
179
+ .empty {
180
+ padding: 16px;
181
+ font-size: 12px;
182
+ font-weight: 500;
183
+ color: var(--c-text-2);
184
+
185
+ .search + & {
186
+ border-top: 1px solid var(--c-divider-light);
187
+ }
188
+ }
189
+ </style>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { DropdownSectionFilterOption } from '../composables/Dropdown'
3
+ import SDropdownSectionFilterItemAvatar from './SDropdownSectionFilterItemAvatar.vue'
4
+ import SDropdownSectionFilterItemText from './SDropdownSectionFilterItemText.vue'
5
+
6
+ defineProps<{
7
+ option: DropdownSectionFilterOption
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <SDropdownSectionFilterItemText
13
+ v-if="option.type === 'text' || option.type === undefined"
14
+ :label="option.label"
15
+ />
16
+ <SDropdownSectionFilterItemAvatar
17
+ v-else-if="option.type === 'avatar'"
18
+ :label="option.label"
19
+ :image="option.image"
20
+ />
21
+ </template>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import SAvatar from './SAvatar.vue'
3
+
4
+ defineProps<{
5
+ label: string
6
+ image?: string | null
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <span class="SDropdownSectionFilterItemAvatar">
12
+ <SAvatar size="nano" :avatar="image" :name="label" />
13
+ <span class="name">{{ label }}</span>
14
+ </span>
15
+ </template>
16
+
17
+ <style scoped lang="postcss">
18
+ .SDropdownSectionFilterItemAvatar {
19
+ display: inline-flex;
20
+ align-items: center;
21
+ padding-left: 8px;
22
+ min-height: 32px;
23
+ }
24
+
25
+ .name {
26
+ display: inline-block;
27
+ padding-left: 8px;
28
+ font-size: 12px;
29
+ font-weight: 500;
30
+ }
31
+ </style>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ label: string
4
+ }>()
5
+ </script>
6
+
7
+ <template>
8
+ <span class="SDropdownSectionFilterItemText">
9
+ {{ label }}
10
+ </span>
11
+ </template>
12
+
13
+ <style scoped lang="postcss">
14
+ .SDropdownSectionFilterItemText {
15
+ padding-left: 8px;
16
+ line-height: 32px;
17
+ font-size: 12px;
18
+ font-weight: 500;
19
+ }
20
+ </style>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import { DropdownSectionMenuOption } from '../composables/Dropdown'
3
+
4
+ defineProps<{
5
+ options: DropdownSectionMenuOption[]
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <ul class="SDropdownSectionMenu">
11
+ <li v-for="option in options" :key="option.label" class="item">
12
+ <button class="button" @click="option.onClick">
13
+ {{ option.label }}
14
+ </button>
15
+ </li>
16
+ </ul>
17
+ </template>
18
+
19
+ <style scoped lang="postcss">
20
+ .SDropdownSectionMenu {
21
+ padding: 8px;
22
+ }
23
+
24
+ .button {
25
+ display: block;
26
+ border-radius: 6px;
27
+ padding: 0 8px;
28
+ width: 100%;
29
+ text-align: left;
30
+ line-height: 32px;
31
+ font-size: 12px;
32
+ font-weight: 500;
33
+ transition: color 0.25s, background-color 0.25s;
34
+
35
+ &:hover {
36
+ background-color: var(--c-bg-elv-down);
37
+ }
38
+ }
39
+ </style>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import { Icon, IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import type { DefineComponent } from 'vue'
4
+
5
+ defineProps<{
6
+ icon: IconifyIcon | DefineComponent
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <Icon v-if="typeof icon['body'] === 'string'" :icon="(icon as IconifyIcon)" />
12
+ <component v-else :is="icon" />
13
+ </template>