@raclettejs/workbench 0.1.19 → 0.1.21
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.
- package/.raclette/backend/raclette.config.js +519 -0
- package/{yarn.lock → .raclette/backend/yarn.lock} +1014 -796
- package/.raclette/frontend/raclette.config.js +47 -0
- package/.raclette/frontend/yarn.lock +3428 -0
- package/.raclette/raclette.config.js +643 -0
- package/.raclette/virtual/backend/README.md +172 -0
- package/.raclette/virtual/backend/raclette.config.js +519 -0
- package/.raclette/virtual/backend/yarn.lock +3773 -0
- package/.raclette/virtual/frontend/README.md +511 -0
- package/.raclette/virtual/frontend/raclette.config.js +47 -0
- package/.raclette/virtual/frontend/src/core/lib/eggs/readme.md +75 -0
- package/.raclette/virtual/frontend/yarn.lock +3428 -0
- package/CHANGELOG.md +7 -0
- package/i18n/de-DE.json +17 -1
- package/i18n/en-EU.json +17 -1
- package/i18n/sk-SK.json +17 -1
- package/package.json +4 -2
- package/packages.json +3 -1
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +8 -3
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +60 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +4 -7
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +34 -117
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListFlat.vue +20 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListGrouped.vue +50 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListHeader.vue +213 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListItem.vue +56 -0
- package/plugins/pacifico__compositions/frontend/composables/useWidgetList.ts +288 -0
- package/raclette.config.js +4 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="tw:sticky tw:top-0 tw:z-10 tw:bg-[rgb(var(--v-theme-surface))] tw:pt-2 tw:pb-2 tw:backdrop-blur-sm"
|
|
4
|
+
>
|
|
5
|
+
<div
|
|
6
|
+
class="tw:flex tw:items-center tw:gap-2 tw:mb-2 tw:px-1 tw:rounded-xl tw:border tw:border-slate-500/60 tw:bg-slate-50/80 tw:dark:bg-slate-900/60"
|
|
7
|
+
>
|
|
8
|
+
<SimpleSearchInput
|
|
9
|
+
:model-value="searchQuery"
|
|
10
|
+
variant="solo"
|
|
11
|
+
class="tw:flex-1"
|
|
12
|
+
:clearable="false"
|
|
13
|
+
:label="
|
|
14
|
+
$t(
|
|
15
|
+
'workbench.compositionConfiguration.widgetLayout.widgetList.searchLabel',
|
|
16
|
+
)
|
|
17
|
+
"
|
|
18
|
+
@update:model-value="onUpdateSearchQuery"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<v-menu :max-width="380" :close-on-content-click="false">
|
|
22
|
+
<template #activator="{ props }">
|
|
23
|
+
<v-btn
|
|
24
|
+
v-bind="props"
|
|
25
|
+
icon
|
|
26
|
+
variant="tonal"
|
|
27
|
+
density="comfortable"
|
|
28
|
+
class="tw:ml-auto tw:border tw:border-slate-200/80 tw:bg-slate-100/80 hover:tw:bg-slate-200/80 tw:dark:bg-slate-800/80 dark:hover:tw:bg-slate-700/80"
|
|
29
|
+
:aria-label="
|
|
30
|
+
$t(
|
|
31
|
+
'workbench.compositionConfiguration.widgetLayout.widgetList.menuAriaLabel',
|
|
32
|
+
)
|
|
33
|
+
"
|
|
34
|
+
>
|
|
35
|
+
<v-icon icon="mdi-tune-variant" />
|
|
36
|
+
</v-btn>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<v-card
|
|
40
|
+
class="tw:min-w-[260px] tw:rounded-xl tw:shadow-lg tw:border tw:border-slate-200/80"
|
|
41
|
+
>
|
|
42
|
+
<v-card-title class="tw:py-2.5 tw:px-3">
|
|
43
|
+
<div
|
|
44
|
+
class="tw:flex tw:items-center tw:justify-between tw:gap-2 tw:text-sm tw:font-medium"
|
|
45
|
+
>
|
|
46
|
+
<span>
|
|
47
|
+
{{
|
|
48
|
+
$t(
|
|
49
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.menuTitle",
|
|
50
|
+
)
|
|
51
|
+
}}
|
|
52
|
+
</span>
|
|
53
|
+
<span
|
|
54
|
+
class="tw:inline-flex tw:items-center tw:rounded-full tw:bg-slate-100/90 tw:px-2 tw:py-[2px] tw:text-[11px] tw:font-medium tw:uppercase tw:tracking-wide tw:text-slate-500 tw:dark:bg-slate-800/80"
|
|
55
|
+
>
|
|
56
|
+
{{ visibleWidgetsCount }}/{{ widgetsCount }}
|
|
57
|
+
</span>
|
|
58
|
+
</div>
|
|
59
|
+
</v-card-title>
|
|
60
|
+
<v-divider />
|
|
61
|
+
|
|
62
|
+
<v-card-text class="tw:!py-2.5 tw:!px-3 tw:space-y-4">
|
|
63
|
+
<div
|
|
64
|
+
class="tw:rounded-lg tw:bg-slate-50/80 tw:dark:bg-slate-900/40 tw:p-2.5"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
class="tw:flex tw:items-center tw:justify-between tw:gap-2 tw:mb-2"
|
|
68
|
+
>
|
|
69
|
+
<div
|
|
70
|
+
class="tw:text-[11px] tw:font-semibold tw:uppercase tw:tracking-wide tw:text-slate-500"
|
|
71
|
+
>
|
|
72
|
+
{{
|
|
73
|
+
$t(
|
|
74
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.sortingTitle",
|
|
75
|
+
)
|
|
76
|
+
}}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<v-chip-group
|
|
80
|
+
:model-value="sortMode"
|
|
81
|
+
column
|
|
82
|
+
class="tw:flex tw:flex-wrap tw:gap-1 tw:max-w-[320px]"
|
|
83
|
+
@update:model-value="onUpdateSortMode"
|
|
84
|
+
mandatory
|
|
85
|
+
>
|
|
86
|
+
<v-chip
|
|
87
|
+
v-for="option in sortOptions"
|
|
88
|
+
:key="option.value"
|
|
89
|
+
:value="option.value"
|
|
90
|
+
size="small"
|
|
91
|
+
variant="outlined"
|
|
92
|
+
class="tw:text-xs tw:font-medium tw:rounded-full tw:border-slate-200/80 tw:bg-white/90 tw:dark:bg-slate-900/60"
|
|
93
|
+
color="primary"
|
|
94
|
+
>
|
|
95
|
+
{{ $t(option.labelKey) }}
|
|
96
|
+
</v-chip>
|
|
97
|
+
</v-chip-group>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div
|
|
101
|
+
class="tw:rounded-lg tw:bg-slate-50/80 tw:dark:bg-slate-900/40 tw:px-2.5 tw:py-2.5"
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
class="tw:flex tw:items-center tw:justify-between tw:gap-2 tw:mb-2"
|
|
105
|
+
>
|
|
106
|
+
<div
|
|
107
|
+
class="tw:text-[11px] tw:font-semibold tw:uppercase tw:tracking-wide tw:text-slate-500"
|
|
108
|
+
>
|
|
109
|
+
{{
|
|
110
|
+
$t(
|
|
111
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.pluginFilterTitle",
|
|
112
|
+
)
|
|
113
|
+
}}
|
|
114
|
+
</div>
|
|
115
|
+
<div class="tw:flex tw:items-center tw:gap-2">
|
|
116
|
+
<span
|
|
117
|
+
class="tw:inline-flex tw:items-center tw:rounded-full tw:bg-slate-100/90 tw:px-2 tw:py-[2px] tw:text-[11px] tw:font-medium tw:uppercase tw:tracking-wide tw:text-slate-500 tw:dark:bg-slate-800/80"
|
|
118
|
+
>
|
|
119
|
+
{{ visiblePluginKeysModel.length }}/{{
|
|
120
|
+
pluginFilterOptions.length
|
|
121
|
+
}}
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="tw:max-h-64 tw:overflow-y-auto tw:pr-1">
|
|
127
|
+
<v-chip-group
|
|
128
|
+
v-model="visiblePluginKeysModel"
|
|
129
|
+
multiple
|
|
130
|
+
column
|
|
131
|
+
class="tw:flex tw:flex-wrap tw:gap-1"
|
|
132
|
+
>
|
|
133
|
+
<v-chip
|
|
134
|
+
v-for="plugin in pluginFilterOptions"
|
|
135
|
+
:key="plugin.key"
|
|
136
|
+
:value="plugin.key"
|
|
137
|
+
size="small"
|
|
138
|
+
variant="outlined"
|
|
139
|
+
filter
|
|
140
|
+
color="primary"
|
|
141
|
+
class="tw:text-[11px] tw:font-medium tw:rounded-full tw:border-slate-200/80 tw:bg-white/90 tw:dark:bg-slate-900/60"
|
|
142
|
+
>
|
|
143
|
+
{{ plugin.label }}
|
|
144
|
+
</v-chip>
|
|
145
|
+
</v-chip-group>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</v-card-text>
|
|
149
|
+
</v-card>
|
|
150
|
+
</v-menu>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div
|
|
154
|
+
v-if="widgetsCount > 0"
|
|
155
|
+
class="tw:flex tw:items-center tw:justify-between tw:px-1.5 tw:pt-0.5 tw:text-[11px] tw:text-slate-500"
|
|
156
|
+
>
|
|
157
|
+
<span>
|
|
158
|
+
<template v-if="visibleWidgetsCount === widgetsCount">
|
|
159
|
+
{{
|
|
160
|
+
$t(
|
|
161
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.countAll",
|
|
162
|
+
{ count: widgetsCount },
|
|
163
|
+
)
|
|
164
|
+
}}
|
|
165
|
+
</template>
|
|
166
|
+
<template v-else>
|
|
167
|
+
{{
|
|
168
|
+
$t(
|
|
169
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.countFiltered",
|
|
170
|
+
{ visible: visibleWidgetsCount, total: widgetsCount },
|
|
171
|
+
)
|
|
172
|
+
}}
|
|
173
|
+
</template>
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<script setup lang="ts">
|
|
180
|
+
import { computed } from "vue"
|
|
181
|
+
import SimpleSearchInput from "@raclettejs/core/orchestrator/components/input/SimpleSearchInput.vue"
|
|
182
|
+
import type { SortMode, SortOption } from "../../../composables/useWidgetList"
|
|
183
|
+
|
|
184
|
+
const props = defineProps<{
|
|
185
|
+
searchQuery: string
|
|
186
|
+
sortMode: SortMode
|
|
187
|
+
sortOptions: SortOption[]
|
|
188
|
+
pluginFilterOptions: { key: string; label: string }[]
|
|
189
|
+
visiblePluginKeys: string[]
|
|
190
|
+
widgetsCount: number
|
|
191
|
+
visibleWidgetsCount: number
|
|
192
|
+
}>()
|
|
193
|
+
|
|
194
|
+
const emit = defineEmits<{
|
|
195
|
+
(e: "update:searchQuery", value: string): void
|
|
196
|
+
(e: "update:sortMode", value: SortMode): void
|
|
197
|
+
(e: "update:visiblePluginKeys", value: string[]): void
|
|
198
|
+
}>()
|
|
199
|
+
|
|
200
|
+
const visiblePluginKeysModel = computed({
|
|
201
|
+
get: () => props.visiblePluginKeys,
|
|
202
|
+
set: (value: string[]) => emit("update:visiblePluginKeys", value),
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
const onUpdateSearchQuery = (value: string) => {
|
|
206
|
+
emit("update:searchQuery", value)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const onUpdateSortMode = (value: SortMode | null) => {
|
|
210
|
+
if (value === null) return
|
|
211
|
+
emit("update:sortMode", value)
|
|
212
|
+
}
|
|
213
|
+
</script>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DraggableElement
|
|
3
|
+
:class="outerClass"
|
|
4
|
+
:draggable-config="{
|
|
5
|
+
type: 'widget',
|
|
6
|
+
widgetName: widget.name,
|
|
7
|
+
pluginKey: widget.pluginKey,
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<div
|
|
11
|
+
class="tw:flex tw:items-center tw:gap-3 tw:px-3 tw:py-2 tw:rounded-lg"
|
|
12
|
+
tabindex="0"
|
|
13
|
+
>
|
|
14
|
+
<div class="tw:flex tw:items-center tw:gap-3 tw:min-w-0">
|
|
15
|
+
<div
|
|
16
|
+
class="tw:flex-shrink-0 tw:w-8 tw:h-8 tw:flex tw:items-center tw:justify-center"
|
|
17
|
+
>
|
|
18
|
+
<WidgetIcon :module="widget" />
|
|
19
|
+
</div>
|
|
20
|
+
<div class="tw:min-w-0">
|
|
21
|
+
<div
|
|
22
|
+
class="tw:text-sm tw:font-medium tw:whitespace-nowrap tw:truncate"
|
|
23
|
+
>
|
|
24
|
+
{{ widget.title }}
|
|
25
|
+
</div>
|
|
26
|
+
<div
|
|
27
|
+
class="tw:text-xs tw:text-slate-500 tw:whitespace-nowrap tw:truncate"
|
|
28
|
+
>
|
|
29
|
+
{{ pluginLabel }} ·
|
|
30
|
+
{{ widget.author }}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</DraggableElement>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import { computed } from "vue"
|
|
40
|
+
import { RegisteredWidgetDeclaration } from "@raclettejs/core"
|
|
41
|
+
import DraggableElement from "@raclettejs/core/orchestrator/components/dragAndDrop/DraggableElement.vue"
|
|
42
|
+
import WidgetIcon from "./WidgetIcon.vue"
|
|
43
|
+
import { deslugify } from "@shared/helper/pluginHelper"
|
|
44
|
+
|
|
45
|
+
const props = defineProps<{
|
|
46
|
+
widget: RegisteredWidgetDeclaration
|
|
47
|
+
outerClass?: string
|
|
48
|
+
}>()
|
|
49
|
+
|
|
50
|
+
const pluginLabel = computed(() => {
|
|
51
|
+
const parts = props.widget.pluginKey.split("__")
|
|
52
|
+
const slug = parts[1] ?? parts[0]
|
|
53
|
+
return deslugify(slug)
|
|
54
|
+
})
|
|
55
|
+
</script>
|
|
56
|
+
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { computed, ref, watch } from "vue"
|
|
2
|
+
import { useFuse } from "@vueuse/integrations/useFuse"
|
|
3
|
+
import useWidgets from "@raclettejs/core/orchestrator/composables/useWidgets"
|
|
4
|
+
import type { RegisteredWidgetDeclaration } from "@raclettejs/core"
|
|
5
|
+
import { deslugify } from "@shared/helper/pluginHelper"
|
|
6
|
+
|
|
7
|
+
export type SortMode = "name" | "author" | "plugin" | "group"
|
|
8
|
+
|
|
9
|
+
export type SortOption = { labelKey: string; value: SortMode }
|
|
10
|
+
|
|
11
|
+
export type GroupedWidgets = Record<
|
|
12
|
+
string,
|
|
13
|
+
Record<string, RegisteredWidgetDeclaration[]>
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
const sortOptions: SortOption[] = [
|
|
17
|
+
{
|
|
18
|
+
labelKey:
|
|
19
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.sortOptions.group",
|
|
20
|
+
value: "group",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
labelKey:
|
|
24
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.sortOptions.name",
|
|
25
|
+
value: "name",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
labelKey:
|
|
29
|
+
"workbench.compositionConfiguration.widgetLayout.widgetList.sortOptions.author",
|
|
30
|
+
value: "author",
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
export function useWidgetList() {
|
|
35
|
+
const { widgets } = useWidgets()
|
|
36
|
+
|
|
37
|
+
const searchQuery = ref("")
|
|
38
|
+
const sortMode = ref<SortMode>("group")
|
|
39
|
+
const visiblePluginKeys = ref<string[]>([])
|
|
40
|
+
|
|
41
|
+
const pluginFilterOptions = computed(() => {
|
|
42
|
+
const map = new Map<string, { key: string; label: string }>()
|
|
43
|
+
|
|
44
|
+
widgets.value.forEach((widget) => {
|
|
45
|
+
if (!map.has(widget.pluginKey)) {
|
|
46
|
+
const parts = widget.pluginKey.split("__")
|
|
47
|
+
const slug = parts[1] ?? parts[0]
|
|
48
|
+
map.set(widget.pluginKey, {
|
|
49
|
+
key: widget.pluginKey,
|
|
50
|
+
label: deslugify(slug),
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return Array.from(map.values()).sort((a, b) =>
|
|
56
|
+
a.label.localeCompare(b.label),
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const widgetsCount = computed(() => widgets.value.length)
|
|
61
|
+
|
|
62
|
+
const { results: fuseResults } = useFuse<RegisteredWidgetDeclaration>(
|
|
63
|
+
searchQuery,
|
|
64
|
+
widgets,
|
|
65
|
+
{
|
|
66
|
+
fuseOptions: {
|
|
67
|
+
keys: ["title", "description", "author", "pluginKey", "name"],
|
|
68
|
+
includeScore: true,
|
|
69
|
+
threshold: 0.4,
|
|
70
|
+
},
|
|
71
|
+
matchAllWhenSearchEmpty: true,
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const visibleWidgetsCount = computed(() => {
|
|
76
|
+
const query = (searchQuery.value ?? "").trim()
|
|
77
|
+
|
|
78
|
+
const baseList = query
|
|
79
|
+
? fuseResults.value.map(
|
|
80
|
+
(result: { item: RegisteredWidgetDeclaration }) => result.item,
|
|
81
|
+
)
|
|
82
|
+
: widgets.value
|
|
83
|
+
|
|
84
|
+
const allowedPlugins = new Set(visiblePluginKeys.value)
|
|
85
|
+
|
|
86
|
+
const filteredByPlugin =
|
|
87
|
+
allowedPlugins.size === 0
|
|
88
|
+
? baseList
|
|
89
|
+
: baseList.filter((widget) => allowedPlugins.has(widget.pluginKey))
|
|
90
|
+
|
|
91
|
+
return filteredByPlugin.length
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
watch(
|
|
95
|
+
() => widgets.value,
|
|
96
|
+
(current) => {
|
|
97
|
+
if (!current.length) return
|
|
98
|
+
if (visiblePluginKeys.value.length) return
|
|
99
|
+
|
|
100
|
+
visiblePluginKeys.value = Array.from(
|
|
101
|
+
new Set(current.map((w) => w.pluginKey)),
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
{ immediate: true },
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const groupedWidgets = computed<GroupedWidgets>(() => {
|
|
108
|
+
const unsortedResult: GroupedWidgets = {}
|
|
109
|
+
|
|
110
|
+
const allowedPlugins = new Set(visiblePluginKeys.value)
|
|
111
|
+
|
|
112
|
+
widgets.value.forEach((widget) => {
|
|
113
|
+
if (allowedPlugins.size && !allowedPlugins.has(widget.pluginKey)) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!unsortedResult[widget.author]) {
|
|
118
|
+
unsortedResult[widget.author] = {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!unsortedResult[widget.author][widget.pluginKey]) {
|
|
122
|
+
unsortedResult[widget.author][widget.pluginKey] = []
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
unsortedResult[widget.author][widget.pluginKey].push(widget)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const sortedAuthors = Object.keys(unsortedResult).sort((a, b) => {
|
|
129
|
+
if (a === "pacifico") {
|
|
130
|
+
return 1
|
|
131
|
+
}
|
|
132
|
+
if (b === "pacifico") {
|
|
133
|
+
return -1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return a.localeCompare(b)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const sortedResult: GroupedWidgets = {}
|
|
140
|
+
|
|
141
|
+
sortedAuthors.forEach((author) => {
|
|
142
|
+
const plugins = Object.keys(unsortedResult[author]).sort((a, b) => {
|
|
143
|
+
if (a === "core") {
|
|
144
|
+
return 1
|
|
145
|
+
}
|
|
146
|
+
if (b === "core") {
|
|
147
|
+
return -1
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return a.localeCompare(b)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
sortedResult[author] = {}
|
|
154
|
+
plugins.forEach((pluginKey) => {
|
|
155
|
+
sortedResult[author][pluginKey] = unsortedResult[author][pluginKey]
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return sortedResult
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const sortWidgets = (
|
|
163
|
+
list: RegisteredWidgetDeclaration[],
|
|
164
|
+
mode: SortMode,
|
|
165
|
+
): RegisteredWidgetDeclaration[] => {
|
|
166
|
+
const copy = [...list]
|
|
167
|
+
|
|
168
|
+
switch (mode) {
|
|
169
|
+
case "name":
|
|
170
|
+
copy.sort((a, b) => a.title.localeCompare(b.title))
|
|
171
|
+
break
|
|
172
|
+
case "author":
|
|
173
|
+
copy.sort((a, b) => a.author.localeCompare(b.author))
|
|
174
|
+
break
|
|
175
|
+
default:
|
|
176
|
+
break
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return copy
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const filteredWidgetsFlat = computed<RegisteredWidgetDeclaration[]>(() => {
|
|
183
|
+
const query = (searchQuery.value ?? "").trim()
|
|
184
|
+
|
|
185
|
+
const allowedPlugins = new Set(visiblePluginKeys.value)
|
|
186
|
+
|
|
187
|
+
if (query) {
|
|
188
|
+
const fuseOrdered = fuseResults.value
|
|
189
|
+
.map((result: { item: RegisteredWidgetDeclaration }) => result.item)
|
|
190
|
+
.filter(
|
|
191
|
+
(widget: RegisteredWidgetDeclaration) =>
|
|
192
|
+
allowedPlugins.size === 0 || allowedPlugins.has(widget.pluginKey),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return fuseOrdered
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const baseList = widgets.value
|
|
199
|
+
|
|
200
|
+
const filteredByPlugin =
|
|
201
|
+
allowedPlugins.size === 0
|
|
202
|
+
? baseList
|
|
203
|
+
: baseList.filter((widget) => allowedPlugins.has(widget.pluginKey))
|
|
204
|
+
|
|
205
|
+
if (sortMode.value === "group") {
|
|
206
|
+
return filteredByPlugin
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return sortWidgets(filteredByPlugin, sortMode.value)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const filteredGroupedWidgets = computed<GroupedWidgets>(() => {
|
|
213
|
+
const query = (searchQuery.value ?? "").trim()
|
|
214
|
+
|
|
215
|
+
if (!query) {
|
|
216
|
+
return groupedWidgets.value
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const allowedPlugins = new Set(visiblePluginKeys.value)
|
|
220
|
+
|
|
221
|
+
const matchedWidgets = fuseResults.value
|
|
222
|
+
.map((result: { item: RegisteredWidgetDeclaration }) => result.item)
|
|
223
|
+
.filter(
|
|
224
|
+
(widget: RegisteredWidgetDeclaration) =>
|
|
225
|
+
allowedPlugins.size === 0 || allowedPlugins.has(widget.pluginKey),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
const unsortedResult: GroupedWidgets = {}
|
|
229
|
+
|
|
230
|
+
matchedWidgets.forEach((widget: RegisteredWidgetDeclaration) => {
|
|
231
|
+
if (!unsortedResult[widget.author]) {
|
|
232
|
+
unsortedResult[widget.author] = {}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!unsortedResult[widget.author][widget.pluginKey]) {
|
|
236
|
+
unsortedResult[widget.author][widget.pluginKey] = []
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
unsortedResult[widget.author][widget.pluginKey].push(widget)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const sortedAuthors = Object.keys(unsortedResult).sort((a, b) => {
|
|
243
|
+
if (a === "pacifico") {
|
|
244
|
+
return 1
|
|
245
|
+
}
|
|
246
|
+
if (b === "pacifico") {
|
|
247
|
+
return -1
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return a.localeCompare(b)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const sortedResult: GroupedWidgets = {}
|
|
254
|
+
|
|
255
|
+
sortedAuthors.forEach((author) => {
|
|
256
|
+
const plugins = Object.keys(unsortedResult[author]).sort((a, b) => {
|
|
257
|
+
if (a === "core") {
|
|
258
|
+
return 1
|
|
259
|
+
}
|
|
260
|
+
if (b === "core") {
|
|
261
|
+
return -1
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return a.localeCompare(b)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
sortedResult[author] = {}
|
|
268
|
+
plugins.forEach((pluginKey) => {
|
|
269
|
+
sortedResult[author][pluginKey] = unsortedResult[author][pluginKey]
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
return sortedResult
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
widgets,
|
|
278
|
+
searchQuery,
|
|
279
|
+
sortMode,
|
|
280
|
+
visiblePluginKeys,
|
|
281
|
+
sortOptions,
|
|
282
|
+
pluginFilterOptions,
|
|
283
|
+
widgetsCount,
|
|
284
|
+
visibleWidgetsCount,
|
|
285
|
+
filteredWidgetsFlat,
|
|
286
|
+
filteredGroupedWidgets,
|
|
287
|
+
}
|
|
288
|
+
}
|
package/raclette.config.js
CHANGED
|
@@ -4,7 +4,7 @@ import interactionLinks from "./config/interactionLinks.js"
|
|
|
4
4
|
|
|
5
5
|
export default defineRacletteConfig({
|
|
6
6
|
name: "raclette-workbench",
|
|
7
|
-
|
|
7
|
+
root: ".raclette",
|
|
8
8
|
// Configure services
|
|
9
9
|
services: {
|
|
10
10
|
frontend: {
|
|
@@ -144,4 +144,7 @@ export default defineRacletteConfig({
|
|
|
144
144
|
eslint: {
|
|
145
145
|
useRecommended: true,
|
|
146
146
|
},
|
|
147
|
+
global: {
|
|
148
|
+
requiresAuthentication: true
|
|
149
|
+
}
|
|
147
150
|
})
|