@opentiny/tiny-robot 0.1.0 → 0.2.0-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 (150) hide show
  1. package/dist/action-group/ActionGroup.vue.d.ts +26 -0
  2. package/dist/action-group/ActionGroupItem.vue.d.ts +18 -0
  3. package/dist/action-group/index.d.ts +12 -0
  4. package/dist/action-group/index.type.d.ts +16 -0
  5. package/dist/bubble/index.type.d.ts +1 -1
  6. package/dist/container/index.d.ts +7 -0
  7. package/dist/container/index.type.d.ts +16 -0
  8. package/dist/container/index.vue.d.ts +26 -0
  9. package/dist/feedback/components/SourceList.vue.d.ts +11 -0
  10. package/dist/feedback/components/index.d.ts +1 -0
  11. package/dist/feedback/index.d.ts +7 -0
  12. package/dist/feedback/index.type.d.ts +25 -0
  13. package/dist/feedback/index.vue.d.ts +13 -0
  14. package/dist/history/components/index.d.ts +2 -0
  15. package/dist/history/components/item-tag.vue.d.ts +5 -0
  16. package/dist/history/components/search-empty.vue.d.ts +7 -0
  17. package/dist/history/composables/index.d.ts +1 -0
  18. package/dist/history/composables/useEditItemTitle.d.ts +12 -0
  19. package/dist/history/index.d.ts +6 -0
  20. package/dist/history/index.type.d.ts +43 -0
  21. package/dist/history/index.vue.d.ts +2 -0
  22. package/dist/icon-button/index.d.ts +7 -0
  23. package/dist/icon-button/index.type.d.ts +7 -0
  24. package/dist/icon-button/index.vue.d.ts +6 -0
  25. package/dist/index.d.ts +12 -2
  26. package/dist/index.js +56 -22
  27. package/dist/node_modules/.pnpm/@opentiny_utils@3.22.0/node_modules/@opentiny/utils/dist/index.es.js +1335 -884
  28. package/dist/node_modules/.pnpm/@opentiny_vue-common@3.22.0/node_modules/@opentiny/vue-common/lib/index.js +660 -144
  29. package/dist/node_modules/.pnpm/@opentiny_vue-hooks@3.22.0/node_modules/@opentiny/vue-hooks/dist/src/vue-popper.js +85 -0
  30. package/dist/node_modules/.pnpm/@opentiny_vue-locale@3.22.0/node_modules/@opentiny/vue-locale/lib/index.js +1783 -0
  31. package/dist/node_modules/.pnpm/@opentiny_vue-renderless@3.22.0/node_modules/@opentiny/vue-renderless/tooltip/index.js +77 -0
  32. package/dist/node_modules/.pnpm/@opentiny_vue-renderless@3.22.0/node_modules/@opentiny/vue-renderless/tooltip/vue.js +90 -0
  33. package/dist/node_modules/.pnpm/@opentiny_vue-tooltip@3.22.0/node_modules/@opentiny/vue-tooltip/lib/index.js +176 -0
  34. package/dist/node_modules/.pnpm/@opentiny_vue-tooltip@3.22.0/node_modules/@opentiny/vue-tooltip/lib/pc.js +248 -0
  35. package/dist/node_modules/.pnpm/@vueuse_core@13.1.0_vue@3.5.13/node_modules/@vueuse/core/index.js +190 -0
  36. package/dist/node_modules/.pnpm/@vueuse_shared@13.1.0_vue@3.5.13/node_modules/@vueuse/shared/index.js +53 -0
  37. package/dist/packages/components/src/action-group/ActionGroup.vue.js +7 -0
  38. package/dist/packages/components/src/action-group/ActionGroup.vue2.js +97 -0
  39. package/dist/packages/components/src/action-group/ActionGroupItem.vue.js +14 -0
  40. package/dist/packages/components/src/action-group/ActionGroupItem.vue2.js +4 -0
  41. package/dist/packages/components/src/action-group/index.js +17 -0
  42. package/dist/packages/components/src/bubble/bubble-list.vue.js +2 -2
  43. package/dist/packages/components/src/bubble/bubble-list.vue2.js +18 -19
  44. package/dist/packages/components/src/bubble/bubble.vue.js +2 -2
  45. package/dist/packages/components/src/bubble/bubble.vue2.js +46 -46
  46. package/dist/packages/components/src/container/index.js +9 -0
  47. package/dist/packages/components/src/container/index.vue.js +7 -0
  48. package/dist/packages/components/src/container/index.vue2.js +55 -0
  49. package/dist/packages/components/src/feedback/components/SourceList.vue.js +7 -0
  50. package/dist/packages/components/src/feedback/components/SourceList.vue2.js +52 -0
  51. package/dist/packages/components/src/feedback/index.js +9 -0
  52. package/dist/packages/components/src/feedback/index.vue.js +7 -0
  53. package/dist/packages/components/src/feedback/index.vue2.js +142 -0
  54. package/dist/packages/components/src/history/components/item-tag.vue.js +7 -0
  55. package/dist/packages/components/src/history/components/item-tag.vue2.js +21 -0
  56. package/dist/packages/components/src/history/components/search-empty.vue.js +7 -0
  57. package/dist/packages/components/src/history/components/search-empty.vue2.js +20 -0
  58. package/dist/packages/components/src/history/composables/useEditItemTitle.js +43 -0
  59. package/dist/packages/components/src/history/index.js +11 -0
  60. package/dist/packages/components/src/history/index.vue.js +7 -0
  61. package/dist/packages/components/src/history/index.vue2.js +130 -0
  62. package/dist/packages/components/src/icon-button/index.js +9 -0
  63. package/dist/packages/components/src/icon-button/index.vue.js +7 -0
  64. package/dist/packages/components/src/icon-button/index.vue2.js +40 -0
  65. package/dist/packages/components/src/prompts/prompt.vue.js +2 -2
  66. package/dist/packages/components/src/prompts/prompt.vue2.js +17 -15
  67. package/dist/packages/components/src/question/components/HotQuestions.vue.js +22 -22
  68. package/dist/packages/components/src/question/index.vue.js +7 -7
  69. package/dist/packages/components/src/sender/components/TemplateEditor.vue.js +7 -0
  70. package/dist/packages/components/src/sender/components/TemplateEditor.vue2.js +121 -0
  71. package/dist/packages/components/src/sender/index.vue.js +149 -128
  72. package/dist/packages/components/src/suggestion/components/CategoryNav.vue.js +38 -0
  73. package/dist/packages/components/src/suggestion/components/CategoryNav.vue2.js +4 -0
  74. package/dist/packages/components/src/suggestion/components/SuggestionCapsule.vue.js +107 -0
  75. package/dist/packages/components/src/suggestion/components/SuggestionCapsule.vue2.js +4 -0
  76. package/dist/packages/components/src/suggestion/components/SuggestionPanel.vue.js +123 -0
  77. package/dist/packages/components/src/suggestion/components/SuggestionPanel.vue2.js +4 -0
  78. package/dist/packages/components/src/suggestion/composables/useKeyboardNavigation.js +45 -0
  79. package/dist/packages/components/src/suggestion/composables/useTriggerDetection.js +17 -0
  80. package/dist/packages/components/src/suggestion/index.js +9 -0
  81. package/dist/packages/components/src/suggestion/index.vue.js +179 -0
  82. package/dist/packages/components/src/suggestion/index.vue2.js +4 -0
  83. package/dist/packages/components/src/suggestion/utils/dom.js +18 -0
  84. package/dist/packages/svgs/dist/tiny-robot-svgs.js +364 -69
  85. package/dist/question/components/HotQuestions.vue.d.ts +2 -2
  86. package/dist/question/index.vue.d.ts +1 -1
  87. package/dist/sender/components/ActionButtons.vue.d.ts +2 -2
  88. package/dist/sender/components/TemplateEditor.vue.d.ts +18 -0
  89. package/dist/sender/index.type.d.ts +47 -0
  90. package/dist/sender/index.vue.d.ts +70 -5
  91. package/dist/style.css +1 -1
  92. package/dist/suggestion/components/CategoryNav.vue.d.ts +45 -0
  93. package/dist/suggestion/components/SuggestionCapsule.vue.d.ts +32 -0
  94. package/dist/suggestion/components/SuggestionPanel.vue.d.ts +84 -0
  95. package/dist/suggestion/composables/useKeyboardNavigation.d.ts +18 -0
  96. package/dist/suggestion/composables/useSuggestionFilter.d.ts +10 -0
  97. package/dist/suggestion/composables/useTriggerDetection.d.ts +11 -0
  98. package/dist/suggestion/index.d.ts +7 -0
  99. package/dist/suggestion/index.type.d.ts +94 -0
  100. package/dist/suggestion/index.vue.d.ts +343 -0
  101. package/dist/suggestion/utils/dom.d.ts +20 -0
  102. package/package.json +5 -5
  103. package/src/action-group/ActionGroup.vue +232 -0
  104. package/src/action-group/ActionGroupItem.vue +9 -0
  105. package/src/action-group/index.ts +25 -0
  106. package/src/action-group/index.type.ts +20 -0
  107. package/src/bubble/bubble-list.vue +1 -3
  108. package/src/bubble/bubble.vue +4 -14
  109. package/src/bubble/index.type.ts +1 -1
  110. package/src/container/index.ts +12 -0
  111. package/src/container/index.type.ts +17 -0
  112. package/src/container/index.vue +134 -0
  113. package/src/feedback/components/SourceList.vue +112 -0
  114. package/src/feedback/components/index.ts +1 -0
  115. package/src/feedback/index.ts +12 -0
  116. package/src/feedback/index.type.ts +27 -0
  117. package/src/feedback/index.vue +166 -0
  118. package/src/history/components/index.ts +2 -0
  119. package/src/history/components/item-tag.vue +49 -0
  120. package/src/history/components/search-empty.vue +38 -0
  121. package/src/history/composables/index.ts +1 -0
  122. package/src/history/composables/useEditItemTitle.ts +75 -0
  123. package/src/history/index.ts +12 -0
  124. package/src/history/index.type.ts +50 -0
  125. package/src/history/index.vue +292 -0
  126. package/src/icon-button/index.ts +12 -0
  127. package/src/icon-button/index.type.ts +8 -0
  128. package/src/icon-button/index.vue +52 -0
  129. package/src/index.ts +37 -2
  130. package/src/prompts/prompt.vue +7 -21
  131. package/src/question/components/HotQuestions.vue +1 -1
  132. package/src/question/index.less +9 -10
  133. package/src/sender/components/TemplateEditor.vue +274 -0
  134. package/src/sender/index.less +17 -7
  135. package/src/sender/index.type.ts +51 -0
  136. package/src/sender/index.vue +56 -8
  137. package/src/sender/vars.less +3 -3
  138. package/src/suggestion/components/CategoryNav.vue +38 -0
  139. package/src/suggestion/components/SuggestionCapsule.vue +183 -0
  140. package/src/suggestion/components/SuggestionPanel.vue +147 -0
  141. package/src/suggestion/composables/useKeyboardNavigation.ts +101 -0
  142. package/src/suggestion/composables/useSuggestionFilter.ts +34 -0
  143. package/src/suggestion/composables/useTriggerDetection.ts +46 -0
  144. package/src/suggestion/index.less +497 -0
  145. package/src/suggestion/index.ts +12 -0
  146. package/src/suggestion/index.type.ts +101 -0
  147. package/src/suggestion/index.vue +338 -0
  148. package/src/suggestion/utils/dom.ts +66 -0
  149. package/src/suggestion/vars.less +141 -0
  150. package/.vscode/extensions.json +0 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/tiny-robot",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-alpha.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -16,14 +16,14 @@
16
16
  "vue": "^3.3.11"
17
17
  },
18
18
  "dependencies": {
19
- "@opentiny/tiny-robot-svgs": "0.1.0",
19
+ "@opentiny/tiny-robot-svgs": "0.2.0-alpha.1",
20
20
  "@opentiny/vue": "^3.21.0",
21
21
  "@opentiny/vue-button": "^3.21.0",
22
22
  "@opentiny/vue-icon": "^3.22.0",
23
23
  "@opentiny/vue-input": "^3.21.0",
24
24
  "@opentiny/vue-tooltip": "^3.22.0",
25
- "markdown-it": "^14.1.0",
26
- "vue": "^3.3.11"
25
+ "@vueuse/core": "^13.1.0",
26
+ "markdown-it": "^14.1.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@opentiny/vue-vite-import": "^1.3.0",
@@ -40,5 +40,5 @@
40
40
  "vue": "^3.3.11",
41
41
  "vue-tsc": "^2.2.8"
42
42
  },
43
- "gitHead": "ff5f5ae32d52a6298401cbd9c7f70f0b0c5253e3"
43
+ "gitHead": "3000951723ed8b1caa1361ebac131229bec9c237"
44
44
  }
@@ -0,0 +1,232 @@
1
+ <script setup lang="ts">
2
+ import { IconMenu } from '@opentiny/tiny-robot-svgs'
3
+ import { onClickOutside, useWindowSize } from '@vueuse/core'
4
+ import { computed, nextTick, ref, useTemplateRef, VNode, watch } from 'vue'
5
+ import IconButton from '../icon-button'
6
+ import { ActionGroupEvents, ActionGroupProps, ActionGroupSlots } from './index.type'
7
+
8
+ const props = defineProps<ActionGroupProps>()
9
+
10
+ const slots = defineSlots<ActionGroupSlots>()
11
+
12
+ const emit = defineEmits<ActionGroupEvents>()
13
+
14
+ const children = computed(() => {
15
+ const defaultSlot = slots.default()
16
+
17
+ if (Array.isArray(defaultSlot)) {
18
+ if (defaultSlot.length === 1 && defaultSlot[0].type?.toString() === 'Symbol(v-fgt)') {
19
+ return defaultSlot[0].children as VNode[]
20
+ }
21
+
22
+ return defaultSlot
23
+ }
24
+
25
+ if (defaultSlot.type?.toString() === 'Symbol(v-fgt)') {
26
+ return defaultSlot.children as VNode[]
27
+ }
28
+
29
+ return [defaultSlot]
30
+ })
31
+
32
+ const maxNum = computed(() => {
33
+ const n = props.maxNum ?? Number.MAX_SAFE_INTEGER
34
+
35
+ return n > 0 ? n : Number.MAX_SAFE_INTEGER
36
+ })
37
+
38
+ const showMore = computed(() => {
39
+ return children.value.length > maxNum.value
40
+ })
41
+
42
+ const list = computed(() => {
43
+ if (showMore.value) {
44
+ return children.value.slice(0, maxNum.value)
45
+ }
46
+
47
+ return children.value
48
+ })
49
+
50
+ const moreList = computed(() => {
51
+ if (showMore.value) {
52
+ return children.value.slice(maxNum.value)
53
+ }
54
+
55
+ return []
56
+ })
57
+
58
+ const moreBtn = useTemplateRef('moreBtnRef')
59
+ const dropDown = useTemplateRef('dropDownRef')
60
+ const showDropdown = ref(false)
61
+
62
+ const handleMoreClick = () => {
63
+ showDropdown.value = !showDropdown.value
64
+ }
65
+
66
+ onClickOutside(dropDown, (ev) => {
67
+ if (moreBtn.value?.contains(ev.target as Node)) {
68
+ return
69
+ }
70
+
71
+ showDropdown.value = false
72
+ })
73
+
74
+ const handleItemClick = (name: string) => {
75
+ emit('item-click', name)
76
+ showDropdown.value = false
77
+ }
78
+
79
+ const dropDownPlacement = ref('placement-bottom')
80
+ const { height: windowHeight } = useWindowSize()
81
+
82
+ const updateDropDownPlacement = () => {
83
+ if (!dropDown.value || !moreBtn.value) {
84
+ return 'placement-bottom'
85
+ }
86
+
87
+ const dropDownRect = dropDown.value.getBoundingClientRect()
88
+ const moreBtnRect = moreBtn.value.getBoundingClientRect()
89
+
90
+ dropDownPlacement.value =
91
+ moreBtnRect.bottom + dropDownRect.height + 16 > windowHeight.value ? 'placement-top' : 'placement-bottom'
92
+ }
93
+
94
+ watch(showDropdown, (show) => {
95
+ if (show) {
96
+ nextTick(() => {
97
+ updateDropDownPlacement()
98
+ })
99
+ }
100
+ })
101
+
102
+ watch(windowHeight, () => {
103
+ if (showDropdown.value) {
104
+ updateDropDownPlacement()
105
+ }
106
+ })
107
+ </script>
108
+
109
+ <template>
110
+ <div class="tr-action-group">
111
+ <span
112
+ v-for="(item, index) in list"
113
+ :key="index"
114
+ class="tr-action-group__btn-wrapper"
115
+ @click="handleItemClick(item.props?.name)"
116
+ >
117
+ <component :is="item" />
118
+ </span>
119
+ <span v-if="showMore" ref="moreBtnRef" class="tr-action-group__btn-wrapper" @click="handleMoreClick">
120
+ <slot name="moreBtn">
121
+ <icon-button :icon="IconMenu" tooltip="更多" />
122
+ </slot>
123
+ <transition name="tr-action-group-dropdown">
124
+ <ul v-show="showDropdown" ref="dropDownRef" :class="['tr-action-group__dropdown', dropDownPlacement]">
125
+ <li
126
+ class="tr-action-group__dropdown-item"
127
+ v-for="(item, index) in moreList"
128
+ :key="index"
129
+ @click.stop="handleItemClick(item.props?.name)"
130
+ >
131
+ <component v-if="!props.dropDownShowLabelOnly" :is="item" />
132
+ <span class="tr-action-group__dropdown-item-text">{{ item.props?.label }}</span>
133
+ </li>
134
+ </ul>
135
+ </transition>
136
+ </span>
137
+ </div>
138
+ </template>
139
+
140
+ <style lang="less" scoped>
141
+ .tr-action-group {
142
+ display: inline-flex;
143
+ align-items: center;
144
+ gap: 4px;
145
+
146
+ .tr-action-group__btn-wrapper {
147
+ display: inline-flex;
148
+ line-height: 0;
149
+ position: relative;
150
+ }
151
+
152
+ .tr-action-group__dropdown {
153
+ width: max-content;
154
+ position: absolute;
155
+ z-index: 100;
156
+ right: 0;
157
+ background-color: white;
158
+ padding: 4px;
159
+ border-radius: 12px;
160
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
161
+
162
+ &.placement-top {
163
+ bottom: calc(100% + 8px);
164
+ }
165
+
166
+ &.placement-bottom {
167
+ top: calc(100% + 8px);
168
+ }
169
+
170
+ .tr-action-group__dropdown-item {
171
+ border-radius: 8px;
172
+ cursor: pointer;
173
+ height: 32px;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: flex-start;
177
+ padding: 4px 16px 4px 8px;
178
+ gap: 4px;
179
+ transition: background-color 0.3s;
180
+
181
+ &:not(:first-child) {
182
+ margin-top: 4px;
183
+ }
184
+
185
+ &:hover {
186
+ background-color: rgba(0, 0, 0, 0.04);
187
+ }
188
+
189
+ &:active {
190
+ background-color: rgba(0, 0, 0, 0.15);
191
+ }
192
+
193
+ & > * {
194
+ flex-shrink: 0;
195
+ }
196
+
197
+ &:has(> *:only-child) {
198
+ padding: 4px 16px;
199
+ }
200
+
201
+ .tr-action-group__dropdown-item-text {
202
+ font-size: 12px;
203
+ line-height: 20px;
204
+ color: rgb(25, 25, 25);
205
+ }
206
+
207
+ :deep(button) {
208
+ --tr-icon-button-hover-bg: unset;
209
+ --tr-icon-button-active-bg: unset;
210
+ }
211
+ }
212
+ }
213
+
214
+ // 下拉动画
215
+ .tr-action-group-dropdown {
216
+ &-enter-active,
217
+ &-leave-active {
218
+ transition: opacity 0.3s ease;
219
+ }
220
+
221
+ &-enter-from,
222
+ &-leave-to {
223
+ opacity: 0;
224
+ }
225
+
226
+ &-enter-to,
227
+ &-leave-from {
228
+ opacity: 1;
229
+ }
230
+ }
231
+ }
232
+ </style>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ import { ActionGroupItemProps } from './index.type'
3
+
4
+ defineProps<ActionGroupItemProps>()
5
+ </script>
6
+
7
+ <template>
8
+ <slot></slot>
9
+ </template>
@@ -0,0 +1,25 @@
1
+ import { App } from 'vue'
2
+ import ActionGroup from './ActionGroup.vue'
3
+ import ActionGroupItemComp from './ActionGroupItem.vue'
4
+
5
+ ActionGroup.name = 'TrActionGroup'
6
+
7
+ const install = function <T>(app: App<T>) {
8
+ app.component(ActionGroup.name!, ActionGroup)
9
+ }
10
+
11
+ ActionGroup.install = install
12
+
13
+ export default ActionGroup as typeof ActionGroup & { install: typeof install }
14
+
15
+ ActionGroupItemComp.name = 'TrActionGroupItem'
16
+
17
+ const installActionGroupItem = function <T>(app: App<T>) {
18
+ app.component(ActionGroupItemComp.name!, ActionGroupItemComp)
19
+ }
20
+
21
+ ActionGroupItemComp.install = installActionGroupItem
22
+
23
+ export const ActionGroupItem = ActionGroupItemComp as typeof ActionGroupItemComp & {
24
+ install: typeof installActionGroupItem
25
+ }
@@ -0,0 +1,20 @@
1
+ import { VNode } from 'vue'
2
+
3
+ export interface ActionGroupProps {
4
+ maxNum?: number
5
+ dropDownShowLabelOnly?: boolean
6
+ }
7
+
8
+ export interface ActionGroupEvents {
9
+ (e: 'item-click', name: string): void
10
+ }
11
+
12
+ export interface ActionGroupSlots {
13
+ default: () => VNode | VNode[]
14
+ moreBtn: () => VNode
15
+ }
16
+
17
+ export interface ActionGroupItemProps {
18
+ name: string
19
+ label: string
20
+ }
@@ -21,9 +21,7 @@ watch([() => props.items.length, () => lastBubble.value?.content], () => {
21
21
 
22
22
  const getItemProps = (item: BubbleProps): BubbleProps => {
23
23
  const defaultConfig = item.role ? props.roles?.[item.role] || {} : {}
24
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
- const { id, ...rest } = item
26
- return { ...defaultConfig, ...rest }
24
+ return { ...defaultConfig, ...item }
27
25
  }
28
26
  </script>
29
27
 
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import markdownit from 'markdown-it'
3
- import { computed, defineComponent, useSlots } from 'vue'
4
- import { BubbleActionOptions, BubbleEvents, BubbleProps, BubbleSlots } from './index.type'
3
+ import { computed, useSlots } from 'vue'
5
4
  import { CopyAction, RefreshAction } from './components/actions'
5
+ import { BubbleActionOptions, BubbleEvents, BubbleProps, BubbleSlots } from './index.type'
6
6
 
7
7
  const props = withDefaults(defineProps<BubbleProps>(), {
8
8
  content: '',
@@ -26,16 +26,6 @@ const bubbleContent = computed(() => {
26
26
 
27
27
  const placementStart = computed(() => props.placement === 'start')
28
28
 
29
- const AvatarComp = computed(() => {
30
- if (!props.avatar) {
31
- return null
32
- }
33
-
34
- return defineComponent(() => {
35
- return () => props.avatar
36
- })
37
- })
38
-
39
29
  const defaultActionsMap = new Map<string, BubbleActionOptions>([
40
30
  [
41
31
  'copy',
@@ -99,8 +89,8 @@ const handleActionClick = (name: string, ...args: unknown[]) => {
99
89
  },
100
90
  ]"
101
91
  >
102
- <div v-if="AvatarComp" class="tr-bubble__avatar">
103
- <component :is="AvatarComp"></component>
92
+ <div v-if="props.avatar" class="tr-bubble__avatar">
93
+ <component :is="props.avatar"></component>
104
94
  </div>
105
95
  <div class="tr-bubble__content-wrapper">
106
96
  <slot v-if="props.loading" name="loading">
@@ -16,7 +16,7 @@ export interface BubbleProps {
16
16
  * 气泡内容
17
17
  */
18
18
  content?: string
19
- id?: string
19
+ id?: string | number | symbol
20
20
  /**
21
21
  * 气泡位置
22
22
  */
@@ -0,0 +1,12 @@
1
+ import { App } from 'vue'
2
+ import Container from './index.vue'
3
+
4
+ Container.name = 'TrContainer'
5
+
6
+ const install = function <T>(app: App<T>) {
7
+ app.component(Container.name!, Container)
8
+ }
9
+
10
+ Container.install = install
11
+
12
+ export default Container as typeof Container & { install: typeof install }
@@ -0,0 +1,17 @@
1
+ export interface ContainerProps {
2
+ /**
3
+ * model:show
4
+ */
5
+ show: boolean
6
+ /**
7
+ * model:fullscreen
8
+ */
9
+ fullscreen?: boolean
10
+ }
11
+
12
+ export interface ContainerSlots {
13
+ default: () => unknown
14
+ title: () => unknown
15
+ operations: () => unknown
16
+ footer: () => unknown
17
+ }
@@ -0,0 +1,134 @@
1
+ <script setup lang="ts">
2
+ import { IconCancelFullScreen, IconClose, IconFullScreen } from '@opentiny/tiny-robot-svgs'
3
+ import { computed } from 'vue'
4
+ import { ContainerProps, ContainerSlots } from './index.type'
5
+
6
+ const show = defineModel<ContainerProps['show']>('show', { required: true })
7
+ const fullscreen = defineModel<ContainerProps['fullscreen']>('fullscreen')
8
+
9
+ defineSlots<ContainerSlots>()
10
+
11
+ const IconFullScreenSwitcher = computed(() => (fullscreen.value ? IconCancelFullScreen : IconFullScreen))
12
+ </script>
13
+
14
+ <template>
15
+ <div class="tr-container">
16
+ <div class="tr-container__dragging-bar-wrapper">
17
+ <div class="tr-container__dragging-bar"></div>
18
+ </div>
19
+ <div class="tr-container__header">
20
+ <slot name="title">
21
+ <h3 class="tr-container__title">OpenTiny NEXT</h3>
22
+ </slot>
23
+ <div class="tr-container__header-operations">
24
+ <slot name="operations"></slot>
25
+ <button class="icon-btn" @click="$emit('update:fullscreen', !fullscreen)">
26
+ <IconFullScreenSwitcher />
27
+ </button>
28
+ <button class="icon-btn" @click="$emit('update:show', false)">
29
+ <IconClose />
30
+ </button>
31
+ </div>
32
+ </div>
33
+ <div class="tr-container__body">
34
+ <slot></slot>
35
+ </div>
36
+ <div class="tr-container__footer">
37
+ <slot name="footer"></slot>
38
+ </div>
39
+ </div>
40
+ </template>
41
+
42
+ <style lang="less" scoped>
43
+ .tr-container {
44
+ --tr-container-width: 480px;
45
+
46
+ background-color: rgb(248, 248, 248);
47
+ border: 1px solid rgba(0, 0, 0, 0.08);
48
+ position: fixed;
49
+ top: 0;
50
+ bottom: 0;
51
+ right: 0;
52
+ left: v-bind('fullscreen? "0" : "unset"');
53
+ width: v-bind('fullscreen? "unset" : "var(--tr-container-width)"');
54
+ z-index: v-bind('show? "100":"-1"');
55
+ opacity: v-bind('show? "1":"0"');
56
+ display: flex;
57
+ flex-direction: column;
58
+
59
+ .tr-container__dragging-bar-wrapper {
60
+ flex-shrink: 0;
61
+
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ padding: 6px;
66
+
67
+ // TODO 拖拽条的逻辑
68
+ .tr-container__dragging-bar {
69
+ width: 40px;
70
+ height: 4px;
71
+ background-color: rgb(217, 217, 217);
72
+ border-radius: 999px;
73
+ cursor: grab;
74
+ }
75
+ }
76
+
77
+ .tr-container__header {
78
+ flex-shrink: 0;
79
+
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ padding: 4px 24px;
84
+ }
85
+
86
+ .tr-container__title {
87
+ margin: 0;
88
+ padding: 0;
89
+ color: rgb(25, 25, 25);
90
+ font-weight: 600;
91
+ font-size: 14px;
92
+ line-height: 22px;
93
+ }
94
+
95
+ .tr-container__header-operations {
96
+ display: flex;
97
+ gap: 8px;
98
+
99
+ button.icon-btn {
100
+ width: 28px;
101
+ height: 28px;
102
+ display: inline-flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ border: none;
106
+ border-radius: 8px;
107
+ cursor: pointer;
108
+ padding: 0;
109
+ transition: background-color 0.3s;
110
+
111
+ &:hover {
112
+ background-color: rgba(0, 0, 0, 0.04);
113
+ }
114
+
115
+ &:active {
116
+ background-color: rgba(0, 0, 0, 0.15);
117
+ }
118
+
119
+ svg {
120
+ font-size: 20px;
121
+ }
122
+ }
123
+ }
124
+
125
+ .tr-container__body {
126
+ flex: 1;
127
+ overflow-y: auto;
128
+ }
129
+
130
+ .tr-container__footer {
131
+ flex-shrink: 0;
132
+ }
133
+ }
134
+ </style>
@@ -0,0 +1,112 @@
1
+ <script setup lang="ts">
2
+ import { IconArrowUp } from '@opentiny/tiny-robot-svgs'
3
+ import { computed, nextTick, onMounted, ref, watch } from 'vue'
4
+
5
+ const props = withDefaults(
6
+ defineProps<{
7
+ sources: { label: string; link: string }[]
8
+ linesLimit?: number
9
+ }>(),
10
+ {
11
+ linesLimit: Number.MAX_SAFE_INTEGER,
12
+ },
13
+ )
14
+
15
+ const showAll = ref(false)
16
+ const pillRefs = ref<(HTMLElement | null)[]>([])
17
+ const morePillIndex = ref<number | null>(null)
18
+
19
+ const updateMoreIndex = () => {
20
+ nextTick(() => {
21
+ const tops = pillRefs.value.map((el) => el?.offsetTop || 0)
22
+ const uniqueTops = Array.from(new Set(tops))
23
+
24
+ if (uniqueTops.length > props.linesLimit) {
25
+ // 超过 linesLimit 行,找到第 linesLimit 行最后一个的index
26
+ const lastLineTop = uniqueTops[props.linesLimit - 1]
27
+ const lastIndex = tops.lastIndexOf(lastLineTop)
28
+ morePillIndex.value = lastIndex
29
+ } else {
30
+ morePillIndex.value = null
31
+ }
32
+ })
33
+ }
34
+
35
+ const leftPillCount = computed(() => {
36
+ return props.sources.length - (morePillIndex.value || 0)
37
+ })
38
+
39
+ onMounted(updateMoreIndex)
40
+
41
+ watch(() => props.sources, updateMoreIndex)
42
+
43
+ const setPillRef = (el: HTMLElement | null, idx: number) => {
44
+ if (el) {
45
+ pillRefs.value[idx] = el
46
+ }
47
+ }
48
+ </script>
49
+
50
+ <template>
51
+ <div class="tr-feedback__source-list">
52
+ <template v-for="(source, index) in props.sources" :key="index">
53
+ <a
54
+ v-if="!morePillIndex || showAll || index < morePillIndex"
55
+ class="pill"
56
+ :href="source.link"
57
+ target="_blank"
58
+ :ref="(el) => setPillRef(el as HTMLElement, index)"
59
+ >
60
+ {{ source.label }}
61
+ </a>
62
+ <span v-if="morePillIndex && !showAll && index === morePillIndex" class="pill" @click="showAll = true">
63
+ {{ leftPillCount }}+
64
+ </span>
65
+ </template>
66
+ <span v-if="showAll" class="pill collapse-pill" @click="showAll = false">
67
+ <IconArrowUp />
68
+ </span>
69
+ </div>
70
+ </template>
71
+
72
+ <style lang="less" scoped>
73
+ .tr-feedback__source-list {
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+ gap: 8px;
77
+
78
+ .pill {
79
+ padding: 4px 12px;
80
+ font-size: 12px;
81
+ line-height: 20px;
82
+ border-radius: 999px;
83
+ border: none;
84
+ background-color: rgba(20, 118, 255, 0.06);
85
+ color: rgb(20, 118, 255);
86
+ cursor: pointer;
87
+
88
+ &:hover {
89
+ text-decoration: underline;
90
+ }
91
+
92
+ &.collapse {
93
+ font-size: 16px;
94
+ }
95
+ }
96
+
97
+ .collapse-pill {
98
+ padding: 0;
99
+ font-size: 16px;
100
+ width: 28px;
101
+ height: 28px;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ transition: background-color 0.3s ease;
106
+
107
+ &:hover {
108
+ background-color: rgba(20, 118, 255, 0.12);
109
+ }
110
+ }
111
+ }
112
+ </style>
@@ -0,0 +1 @@
1
+ export { default as SourceList } from './SourceList.vue'
@@ -0,0 +1,12 @@
1
+ import { App } from 'vue'
2
+ import Feedback from './index.vue'
3
+
4
+ Feedback.name = 'TrFeedback'
5
+
6
+ const install = function <T>(app: App<T>) {
7
+ app.component(Feedback.name!, Feedback)
8
+ }
9
+
10
+ Feedback.install = install
11
+
12
+ export default Feedback as typeof Feedback & { install: typeof install }