@raclettejs/core 0.1.10 → 0.1.12

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 (96) hide show
  1. package/CHANGELOG.md +59 -2
  2. package/dist/cli.js +104 -104
  3. package/dist/cli.js.map +7 -0
  4. package/dist/index.js +4 -4
  5. package/dist/index.js.map +7 -0
  6. package/package.json +32 -31
  7. package/services/backend/package.json +35 -45
  8. package/services/backend/src/core/payload/payloadTypes.ts +7 -0
  9. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.get.ts +1 -1
  10. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.getAll.ts +1 -1
  11. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.model.ts +5 -0
  12. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.schema.ts +1 -0
  13. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +1 -1
  14. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.get.ts +1 -1
  15. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.getAll.ts +1 -1
  16. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/helpers/payload.ts +6 -2
  17. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.get.ts +1 -1
  18. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.getAll.ts +1 -1
  19. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.get.ts +1 -1
  20. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.getAll.ts +1 -1
  21. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/index.ts +2 -1
  22. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.get.ts +1 -1
  23. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.getAll.ts +6 -2
  24. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +22 -1
  25. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.get.ts +1 -1
  26. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.getAll.ts +1 -1
  27. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.remove.ts +3 -0
  28. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +1 -2
  29. package/services/backend/src/corePlugins/raclette__core/frontend/generated-config.ts +12 -12
  30. package/services/backend/src/domains/index.ts +3 -0
  31. package/services/backend/src/domains/system/collections.service.ts +119 -0
  32. package/services/backend/src/domains/system/index.ts +8 -0
  33. package/services/backend/src/domains/system/routes/index.ts +5 -0
  34. package/services/backend/src/domains/system/routes/route.collection.getAll.ts +61 -0
  35. package/services/backend/src/domains/system/routes/route.collection.remove.ts +93 -0
  36. package/services/backend/src/types/custom-fastify.d.ts +1 -0
  37. package/services/backend/src/utils/request.utils.ts +28 -1
  38. package/services/backend/yarn.lock +431 -1180
  39. package/services/frontend/package.json +29 -40
  40. package/services/frontend/src/core/constants/ApiClient.ts +6 -1
  41. package/services/frontend/src/core/lib/data/dataApi.ts +8 -1
  42. package/services/frontend/src/core/lib/data/fetchDataHandler.ts +58 -27
  43. package/services/frontend/src/core/lib/data/queryApi.ts +29 -8
  44. package/services/frontend/src/core/lib/data/responseTypeHandler.ts +27 -4
  45. package/services/frontend/src/core/lib/data/writeDataHandler.ts +1 -1
  46. package/services/frontend/src/core/lib/dataHelper.ts +8 -1
  47. package/services/frontend/src/core/lib/easteregg.ts +1 -1
  48. package/services/frontend/src/core/lib/eventEmitter.ts +1 -0
  49. package/services/frontend/src/core/lib/eventWhitelist.ts +11 -1
  50. package/services/frontend/src/core/lib/logger.ts +11 -3
  51. package/services/frontend/src/core/lib/userNotifier.ts +17 -17
  52. package/services/frontend/src/core/main.ts +3 -3
  53. package/services/frontend/src/core/setup/plugin-system/api/wrappers.ts +18 -1
  54. package/services/frontend/src/core/setup/plugin-system/registration/index.ts +1 -1
  55. package/services/frontend/src/core/setup/socketClient.ts +2 -2
  56. package/services/frontend/src/core/setup/socketEvents.ts +2 -2
  57. package/services/frontend/src/core/store/reducers/compositions/selectors.ts +2 -2
  58. package/services/frontend/src/core/store/reducers/data/index.ts +1 -1
  59. package/services/frontend/src/core/store/reducers/data/reducers.ts +3 -3
  60. package/services/frontend/src/core/store/reducers/queries/effects.ts +20 -1
  61. package/services/frontend/src/core/store/reducers/queries/index.ts +1 -0
  62. package/services/frontend/src/core/store/reducers/queries/reducers.ts +28 -1
  63. package/services/frontend/src/core/store/reducers/queriesCache/queryCacheHelper.ts +5 -4
  64. package/services/frontend/src/core/store/reducers/queriesCache/reducers.ts +4 -3
  65. package/services/frontend/src/core/store/state.ts +1 -1
  66. package/services/frontend/src/core/types/ApiClient.ts +1 -1
  67. package/services/frontend/src/core/types/DataApi.ts +4 -0
  68. package/services/frontend/src/core/types/Queries.ts +1 -0
  69. package/services/frontend/src/orchestrator/ProductOrchestrator.vue +19 -7
  70. package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -41
  71. package/services/frontend/src/orchestrator/assets/styles/tailwindStyles.css +3 -0
  72. package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +2 -2
  73. package/services/frontend/src/orchestrator/assets/styles/vuetifyStyles.scss +0 -1
  74. package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +11 -2
  75. package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +167 -58
  76. package/services/frontend/src/orchestrator/components/menu/DevIndicator.vue +122 -7
  77. package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +38 -20
  78. package/services/frontend/src/orchestrator/components/snackbar/SnackStack.vue +17 -5
  79. package/services/frontend/src/orchestrator/composables/useCurrentComposition.ts +32 -4
  80. package/services/frontend/src/orchestrator/composables/usePageNavigation.ts +5 -1
  81. package/services/frontend/src/orchestrator/composables/usePluginApi.ts +125 -26
  82. package/services/frontend/src/orchestrator/composables/useRouteState.ts +0 -1
  83. package/services/frontend/src/orchestrator/composables/useVueQueryObservableHelper.ts +2 -0
  84. package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +5 -3
  85. package/services/frontend/src/orchestrator/composables/useWidgets/helperFunctions.ts +4 -1
  86. package/services/frontend/src/orchestrator/helpers/loginUserNotifications.ts +1 -1
  87. package/services/frontend/src/orchestrator/helpers/uiHelper.ts +1 -1
  88. package/services/frontend/src/orchestrator/i18n/de-DE.json +2 -1
  89. package/services/frontend/src/orchestrator/i18n/en-EU.json +2 -1
  90. package/services/frontend/src/orchestrator/i18n/sk.json +2 -1
  91. package/services/frontend/src/orchestrator/i18n/tl-TL.json +1 -1
  92. package/services/frontend/src/orchestrator/router/routeParserHelper.ts +4 -1
  93. package/services/frontend/src/orchestrator/router/routeStore.ts +78 -11
  94. package/services/frontend/tsconfig.app.json +1 -0
  95. package/services/frontend/yarn.lock +796 -1872
  96. package/yarn.lock +435 -618
@@ -5,18 +5,44 @@ import { useVueWriteOperationHelper } from "@racletteOrchestrator/composables/us
5
5
  import { useVueStateObservableHelper } from "@racletteOrchestrator/composables/useVueStateObservableHelper"
6
6
  import { getCurrentInstance, onUnmounted } from "vue"
7
7
  import { clone } from "ramda"
8
- import { $store } from "@racletteCore"
9
- import type { actionId } from "@racletteCore"
10
- import type { PluginApi, PluginApiParams } from "@racletteOrchestrator"
8
+ import { $store, createStableHash } from "@racletteCore"
9
+ import type { pluginKey } from "@racletteCore"
10
+ import {
11
+ useRouteState,
12
+ type PluginApi,
13
+ type PluginApiParams,
14
+ } from "@racletteOrchestrator"
11
15
  import { useNamespacedI18n } from "@racletteOrchestrator/composables/useNamespacedI18n"
12
- import { fixDataTypesFromObject } from "@racletteCore/lib/dataHelper"
16
+ import { getCompositionByPath } from "@racletteCore/store/reducers/compositions/selectors"
17
+
18
+ //TODO move this into a $store api module
19
+ const findWidgetUUIDs = (obj: any): string[] => {
20
+ const uuids: string[] = []
21
+
22
+ const recurse = (value: any): void => {
23
+ if (Array.isArray(value)) {
24
+ value.forEach(recurse)
25
+ } else if (value && typeof value === "object") {
26
+ if (value.widget?.uuid) {
27
+ uuids.push(value.widget.uuid)
28
+ }
29
+ Object.values(value).forEach(recurse)
30
+ }
31
+ }
32
+
33
+ recurse(obj.widgetsLayout)
34
+ return uuids
35
+ }
13
36
 
14
37
  export const usePluginApi = (
15
- params: actionId | PluginApiParams = {},
38
+ params: pluginKey | PluginApiParams = {},
16
39
  ): PluginApi => {
17
40
  const subscribers$ = {}
41
+
18
42
  const instance = getCurrentInstance()
19
43
  const getState = useVueStateObservableHelper()
44
+
45
+ const routeState = useRouteState()
20
46
  let pluginKeyOverride: string | undefined
21
47
  let actionIdOverride: string | undefined
22
48
 
@@ -80,21 +106,74 @@ export const usePluginApi = (
80
106
  }
81
107
  $instanceApi.$i18n = useNamespacedI18n(pluginKeyOverride ?? pluginKey)
82
108
  if (!pluginKeyOverride) {
109
+ const getSiblingWidgetStates = (compositionId: string = "byRoute") => {
110
+ const currentSlotType = instance.props?.slotType
111
+ let siblingWidgetIds = []
112
+ const activeCompositionPaths = []
113
+
114
+ if (compositionId === "byRoute") {
115
+ const { compositionSlots } = routeState
116
+ compositionSlots.value.forEach((slot) => {
117
+ if (currentSlotType) {
118
+ if (slot.slotName === currentSlotType) {
119
+ activeCompositionPaths.push(slot.slotValue)
120
+ }
121
+ } else {
122
+ activeCompositionPaths.push(slot.slotValue)
123
+ }
124
+ })
125
+ activeCompositionPaths?.forEach((path) => {
126
+ const composition = getCompositionByPath(path)
127
+ siblingWidgetIds = siblingWidgetIds.concat(
128
+ findWidgetUUIDs(composition),
129
+ )
130
+ })
131
+ } else {
132
+ // TODO implement widget retrieval from state via compositionId
133
+ console.log("not implemented yet")
134
+ }
135
+
136
+ if (!siblingWidgetIds.length) {
137
+ console.error("There is no sibling widgets")
138
+ return {}
139
+ } else {
140
+ let lastSubscriber$ =
141
+ subscribers$["$store_getSiblingWidgetStates_" + compositionId]
142
+ const getResult = () => {
143
+ const { state, observable$ } = getState(["widgets", siblingWidgetIds])
144
+ if (lastSubscriber$) {
145
+ lastSubscriber$.unsubscribe()
146
+ }
147
+ lastSubscriber$ = observable$
148
+ subscribers$["$store_getSiblingWidgetStates_" + compositionId] =
149
+ lastSubscriber$
150
+
151
+ return state
152
+ }
153
+ return getResult()
154
+ }
155
+ }
156
+ const getWidgetId = () => {
157
+ return instance?.props?.widgetId || instance?.props?.uuid || ""
158
+ }
83
159
  $instanceApi.$store = {
160
+ hasChanges: $store.hasChanges,
161
+ getSiblingWidgetStates,
84
162
  /* Store getter */
85
- getWidgetState: (widgetId: string = instance?.props?.uuid || "") => {
163
+ getWidgetState: (widgetId: string = getWidgetId()) => {
86
164
  if (!widgetId.length) {
87
165
  console.error("No widgetId was provided")
88
166
  return {}
89
167
  } else {
90
- let lastSubscriber$ = subscribers$["$store_getWidgetState"]
168
+ let lastSubscriber$ =
169
+ subscribers$["$store_getWidgetState_" + widgetId]
91
170
  const getResult = () => {
92
171
  const { state, observable$ } = getState(["widgets", widgetId])
93
172
  if (lastSubscriber$) {
94
173
  lastSubscriber$.unsubscribe()
95
174
  }
96
175
  lastSubscriber$ = observable$
97
- subscribers$["$store_getWidgetState"] = lastSubscriber$
176
+ subscribers$["$store_getWidgetState_" + widgetId] = lastSubscriber$
98
177
 
99
178
  return state
100
179
  }
@@ -117,14 +196,15 @@ export const usePluginApi = (
117
196
  },
118
197
 
119
198
  getQueryState: (actionId) => {
120
- let lastSubscriber$ = subscribers$["$store_getQueryState"]
199
+ if (!actionId) return {}
200
+ let lastSubscriber$ = subscribers$["$store_getQueryState_" + actionId]
121
201
  const getResult = () => {
122
202
  const { state, observable$ } = getState(["queries", actionId])
123
203
  if (lastSubscriber$) {
124
204
  lastSubscriber$.unsubscribe()
125
205
  }
126
206
  lastSubscriber$ = observable$
127
- subscribers$["$store_store_getQueryStateget"] = lastSubscriber$
207
+ subscribers$["$store_getQueryState_" + actionId] = lastSubscriber$
128
208
 
129
209
  return state
130
210
  }
@@ -132,17 +212,19 @@ export const usePluginApi = (
132
212
  },
133
213
 
134
214
  getQueryData: (actionId) => {
215
+ if (!actionId) return {}
135
216
  console.error("not implemented yet")
136
217
  },
137
218
  get: (pathArray, filter) => {
138
- let lastSubscriber$ = subscribers$["$store_get"]
219
+ const cacheString = createStableHash({ pathArray, filter })
220
+ let lastSubscriber$ = subscribers$["$store_get_" + cacheString]
139
221
  const getResult = () => {
140
222
  const { state, observable$ } = getState(pathArray, filter)
141
223
  if (lastSubscriber$) {
142
224
  lastSubscriber$.unsubscribe()
143
225
  }
144
226
  lastSubscriber$ = observable$
145
- subscribers$["$store_get"] = lastSubscriber$
227
+ subscribers$["$store_get_" + cacheString] = lastSubscriber$
146
228
  return state
147
229
  }
148
230
  return getResult()
@@ -153,20 +235,18 @@ export const usePluginApi = (
153
235
  type: "ui/update",
154
236
  payload,
155
237
  }),
156
- setWidgetState: (payload, widgetId = "") => {
157
- const _widgetId =
158
- widgetId.length > 0 ? widgetId : instance?.props?.uuid || false
159
-
238
+ setWidgetState: (payload, widgetId = getWidgetId()) => {
160
239
  $store.dispatch({
161
240
  type: "widgets/update",
162
- uuid: _widgetId,
163
- payload: fixDataTypesFromObject(payload),
241
+ uuid: widgetId,
242
+ override: true,
243
+ payload,
164
244
  })
165
245
  },
166
246
 
167
247
  updateWidgetState: (
168
248
  payload: object,
169
- widgetId: string = instance?.props?.uuid || "",
249
+ widgetId: string = getWidgetId(),
170
250
  ) => {
171
251
  if (!widgetId) return false
172
252
  $store.dispatch({
@@ -175,10 +255,7 @@ export const usePluginApi = (
175
255
  payload: payload,
176
256
  })
177
257
  },
178
- updateConfig: (
179
- payload: object,
180
- widgetId: string = instance?.props?.uuid || "",
181
- ) => {
258
+ updateConfig: (payload: object, widgetId: string = getWidgetId()) => {
182
259
  if (!widgetId) return false
183
260
  $store.dispatch({
184
261
  type: "widgets/update",
@@ -188,19 +265,41 @@ export const usePluginApi = (
188
265
  },
189
266
  })
190
267
  },
191
- updatePublic: (
268
+ updatePublic: (payload: object, widgetId: string = getWidgetId()) => {
269
+ if (!widgetId) return false
270
+ $store.dispatch({
271
+ type: "widgets/update",
272
+ uuid: widgetId,
273
+ payload: {
274
+ public: payload,
275
+ },
276
+ })
277
+ },
278
+ publishWorkingState: (
192
279
  payload: object,
193
- widgetId: string = instance?.props?.uuid || "",
280
+ widgetId: string = getWidgetId(),
194
281
  ) => {
195
282
  if (!widgetId) return false
196
283
  $store.dispatch({
197
284
  type: "widgets/update",
198
285
  uuid: widgetId,
199
286
  payload: {
200
- public: payload,
287
+ workingState: payload,
201
288
  },
202
289
  })
203
290
  },
291
+ updateDataItem: (
292
+ uuid: string,
293
+ payload: object,
294
+ override: boolean = false,
295
+ ) => {
296
+ $store.dispatch({
297
+ type: "data/update",
298
+ docId: uuid,
299
+ override,
300
+ payload: payload,
301
+ })
302
+ },
204
303
  }
205
304
  }
206
305
  const getUser = () => {
@@ -84,7 +84,6 @@ export const useRouteState = () => {
84
84
  const currentSlotParams =
85
85
  compositionSlots.value.find((slot) => slot.slotName === targetSlotName)
86
86
  ?.slotParams ?? {}
87
-
88
87
  // Merge current and requested parameters, then filter to only allowed ones
89
88
  const allowedParams = interactionLink.queryParameters ?? []
90
89
  const mergedParams = mergeDeepRight(currentSlotParams, requestedParams)
@@ -79,6 +79,8 @@ export const useVueQueryObservableHelper = (
79
79
  response: responseRef,
80
80
  execute: coreQuery.execute,
81
81
  uuids: coreQuery.uuids$,
82
+ remove: coreQuery.remove,
83
+ clear: coreQuery.clear,
82
84
  error: computed(() => {
83
85
  return errorObservable.value
84
86
  }),
@@ -66,13 +66,13 @@ export const useVueWriteOperationHelper = (
66
66
  subscriberCounter[instanceActionId] -= 1
67
67
  if (subscriberCounter[instanceActionId] === 0) {
68
68
  // Clean up the observable subject
69
- if (subscriber$[instanceActionId]?.destroy) {
70
- subscriber$[instanceActionId].destroy()
69
+ if (subscriber$[instanceActionId]?.remove) {
70
+ subscriber$[instanceActionId].remove()
71
71
  }
72
72
  delete subscriber$[instanceActionId]
73
73
  delete subscriberCounter[instanceActionId]
74
74
  }
75
- writeOperation.destroy()
75
+ writeOperation.remove()
76
76
  })
77
77
 
78
78
  const operationObject = {
@@ -85,6 +85,8 @@ export const useVueWriteOperationHelper = (
85
85
  }),
86
86
  response: responseRef,
87
87
  execute: writeOperation.execute,
88
+ remove: writeOperation.remove,
89
+ clear: writeOperation.clear,
88
90
  error: computed(() => {
89
91
  return errorObservable.value
90
92
  }),
@@ -19,7 +19,10 @@ const WIDGET_COMPONENT_FILES = import.meta.glob(
19
19
  [
20
20
  "@racletteCore/../../external-app-plugins/**/frontend/widgets/**/*Widget.vue",
21
21
  ],
22
- { eager: false },
22
+ {
23
+ eager: false,
24
+ as: "url",
25
+ },
23
26
  )
24
27
 
25
28
  // Discover widget setup files for metadata
@@ -10,7 +10,7 @@ export default () => {
10
10
  )
11
11
 
12
12
  if (checkTokenNotifications.length) {
13
- $eventbus.emit("ui_addToSnackBar", checkTokenNotifications[0].message)
13
+ $eventbus.emit("ui/addToSnackbar", checkTokenNotifications[0].message)
14
14
  $store.dispatch({
15
15
  type: "notifications/markAllAsRead",
16
16
  })
@@ -2,7 +2,7 @@ import { $eventbus } from "@racletteCore/main"
2
2
 
3
3
  const copyToClipboard = (name: string, value: string) => {
4
4
  navigator.clipboard.writeText(value)
5
- $eventbus.emit("ui_addToSnackBar", {
5
+ $eventbus.emit("ui/addToSnackbar", {
6
6
  i18n: "core.copiedToClipboard$name",
7
7
  content: { name },
8
8
  color: "info",
@@ -107,7 +107,7 @@
107
107
  "color": "Farbe",
108
108
  "versionUpdate": "Eine neue Version ist verfügbar. Laden Sie die Seite neu, um alle neuen Features zu nutzen.",
109
109
  "eggcontrols": {
110
- "asteroids": "Nutzen sie die Pfeiltasten zum Navigieren und die Leertaste zum Schießen. Viel Spaß!"
110
+ "asteroids": "Nutzen sie die Pfeiltasten zum Navigieren und die Leertaste zum Schießen. Mit escape beenden sie das Spiel, Viel Spaß!"
111
111
  },
112
112
  "languages": {
113
113
  "tl-TL": "Klingonisch",
@@ -198,6 +198,7 @@
198
198
  "yi-IL": "Jiddisch (Israel)",
199
199
  "zu-ZA": "Zulu (Südafrika)",
200
200
  "sk": "Slowakisch (Slowakei)",
201
+ "sk-SK": "Slowakisch (Slowakei)",
201
202
  "sl-SL": "Slowenisch (Slowenien)"
202
203
  }
203
204
  }
@@ -107,7 +107,7 @@
107
107
  "color": "Color",
108
108
  "versionUpdate": "A new version is available. Reload the page to use all new features.",
109
109
  "eggcontrols": {
110
- "asteroids": "Use the arrow keys to navigate and the spacebar to shoot. Have fun!"
110
+ "asteroids": "Use the arrow keys to navigate and the spacebar to shoot. Press Escape to exit the game. Have fun!"
111
111
  },
112
112
  "languages": {
113
113
  "tl-TL": "Klingon",
@@ -198,6 +198,7 @@
198
198
  "yi-IL": "Yiddish (Israel)",
199
199
  "zu-ZA": "Zulu (South Africa)",
200
200
  "sk": "Slovak (Slovakia)",
201
+ "sk-SK": "Slovak (Slovakia)",
201
202
  "sl-SL": "Slovenian (Slovenia)"
202
203
  }
203
204
  }
@@ -107,7 +107,7 @@
107
107
  "color": "Farba",
108
108
  "versionUpdate": "Je dostupná nová verzia. Obnovte stránku pre použitie všetkých nových funkcií.",
109
109
  "eggcontrols": {
110
- "asteroids": "Použite šípky na navigáciu a medzerník na streľbu. Bavte sa!"
110
+ "asteroids": "Použite šípky na navigáciu a medzerník na streľbu. Stlačením klávesu Escape ukončíte hru. Bavte sa!"
111
111
  },
112
112
  "languages": {
113
113
  "tl-TL": "Klingončina",
@@ -198,6 +198,7 @@
198
198
  "yi-IL": "Jidiš (Izrael)",
199
199
  "zu-ZA": "Zulu (Južná Afrika)",
200
200
  "sk": "Slovenčina (Slovensko)",
201
+ "sk-SK": "Slovenčina (Slovensko)",
201
202
  "sl-SL": "Slovinčina (Slovinsko)"
202
203
  }
203
204
  }
@@ -107,7 +107,7 @@
107
107
  "color": "ngem",
108
108
  "versionUpdate": "veS wov ’oH. paq reload chel pagh SoH Hoch tlhab!",
109
109
  "eggcontrols": {
110
- "asteroids": "QuQ navmey Dalo'nIS, 'ej bachmeH patlh yIlo'. yIchep!"
110
+ "asteroids": "navigatmeH 'arrow keys' yIlo' 'ej bachmeH 'spacebar' yIlo'. QujmeH 'Escape' yIlo'. yIchegh 'ej yIbel!"
111
111
  }
112
112
  }
113
113
  }
@@ -1,15 +1,19 @@
1
1
  import { compose, type ComposedRouteState } from "./routeParser"
2
2
  import { $store } from "@racletteCore"
3
3
 
4
+ export type WidgetLayoutType = "default" | "spaced" | "card" | "border" | "full"
5
+
4
6
  export interface CompositionSlot {
5
7
  slotName: string
6
8
  slotValue: string
7
9
  slotParams?: Record<string, string>
10
+ slotLayout?: WidgetLayoutType
8
11
  }
9
12
 
10
13
  // TODO: transform this to an array
11
14
  export const DEFAULT_SLOT_NAME = "page"
12
15
  export const DEFAULT_MODAL_SLOT_NAME = "modal"
16
+ export const DEFAULT_INLINE_SLOT_NAME = "inline"
13
17
 
14
18
  export const parseRouteToCompositionSlots = (
15
19
  route: string,
@@ -17,7 +21,6 @@ export const parseRouteToCompositionSlots = (
17
21
  addDefaultSlotIfMissing = false,
18
22
  ) => {
19
23
  const composed = compose(route)
20
-
21
24
  return parseComposedToCompositionSlots(
22
25
  composed,
23
26
  defaultSlotName,
@@ -3,8 +3,8 @@ import type { RouteLocationNormalized } from "vue-router"
3
3
  import {
4
4
  DEFAULT_SLOT_NAME,
5
5
  parseRouteToCompositionSlots,
6
+ type CompositionSlot,
6
7
  } from "@racletteOrchestrator/router/routeParserHelper"
7
- import { difference } from "ramda"
8
8
  import { getCompositionIdByPath } from "@racletteCore/store/reducers/compositions/selectors"
9
9
 
10
10
  // TODO: find better filename
@@ -46,6 +46,59 @@ export const dispatchSetCompositionAs = (
46
46
  }
47
47
  }
48
48
 
49
+ /**
50
+ * Compares two arrays of composition slots and returns the differences
51
+ * Returns an object with three arrays: toRemove, toAdd, toUpdate
52
+ */
53
+ const getCompositionSlotChanges = (
54
+ oldSlots: CompositionSlot[],
55
+ newSlots: CompositionSlot[],
56
+ ) => {
57
+ // Helper: Create a comparison key for slots (ignoring slotParams)
58
+ const getSlotKey = (slot: CompositionSlot) =>
59
+ `${slot.slotName}:${slot.slotValue}`
60
+
61
+ // Helper: Check if slotParams are different
62
+ const hasDifferentSlotParams = (
63
+ slotA: CompositionSlot,
64
+ slotB: CompositionSlot,
65
+ ) => {
66
+ const paramsA = slotA.slotParams || {}
67
+ const paramsB = slotB.slotParams || {}
68
+ return JSON.stringify(paramsA) !== JSON.stringify(paramsB)
69
+ }
70
+
71
+ const oldSlotMap = new Map(oldSlots.map((slot) => [getSlotKey(slot), slot]))
72
+ const newSlotMap = new Map(newSlots.map((slot) => [getSlotKey(slot), slot]))
73
+
74
+ const toRemove: CompositionSlot[] = []
75
+ const toAdd: CompositionSlot[] = []
76
+ const toUpdate: CompositionSlot[] = []
77
+
78
+ // Find slots to remove (exist in old but not in new)
79
+ for (const [key, oldSlot] of oldSlotMap) {
80
+ if (!newSlotMap.has(key)) {
81
+ toRemove.push(oldSlot)
82
+ }
83
+ }
84
+
85
+ // Find slots to add or update (exist in new)
86
+ for (const [key, newSlot] of newSlotMap) {
87
+ if (!oldSlotMap.has(key)) {
88
+ // Completely new slot
89
+ toAdd.push(newSlot)
90
+ } else {
91
+ // Slot exists in both, check if slotParams changed
92
+ const oldSlot = oldSlotMap.get(key)!
93
+ if (hasDifferentSlotParams(oldSlot, newSlot)) {
94
+ toUpdate.push(newSlot)
95
+ }
96
+ }
97
+ }
98
+
99
+ return { toRemove, toAdd, toUpdate }
100
+ }
101
+
49
102
  export const updateActiveCompositions = (
50
103
  to: RouteLocationNormalized,
51
104
  from: RouteLocationNormalized,
@@ -54,7 +107,6 @@ export const updateActiveCompositions = (
54
107
  const hasToPageIdParameter = Boolean(to.params.pageId?.length)
55
108
  const hasFromPageIdParameter = Boolean(from.params.pageId?.length)
56
109
 
57
- // TODO: write helper method to combine compose & parseRouteToCompositionSlots
58
110
  const compositionSlots = parseRouteToCompositionSlots(
59
111
  to.fullPath,
60
112
  undefined,
@@ -67,21 +119,36 @@ export const updateActiveCompositions = (
67
119
  !isInitialRouting && !hasFromPageIdParameter,
68
120
  )
69
121
 
70
- const compositionSlotsToRemove = difference(
122
+ const { toRemove, toAdd, toUpdate } = getCompositionSlotChanges(
71
123
  beforeCompositionSlots,
72
124
  compositionSlots,
73
125
  )
74
- const compositionSlotsToAdd = difference(
75
- compositionSlots,
76
- beforeCompositionSlots,
77
- )
78
126
 
79
- compositionSlotsToRemove.forEach((slot) =>
80
- dispatchSetCompositionAs(slot.slotName),
81
- )
82
- compositionSlotsToAdd.forEach((slot) =>
127
+ // Remove slots that are no longer needed
128
+ toRemove.forEach((slot) => dispatchSetCompositionAs(slot.slotName))
129
+
130
+ // Add completely new slots
131
+ toAdd.forEach((slot) =>
83
132
  dispatchSetCompositionAs(slot.slotName, slot.slotValue, slot.slotParams),
84
133
  )
134
+
135
+ // Update existing slots with new slotParams (without removing/adding)
136
+ toUpdate.forEach((slot) => {
137
+ const compositionId = getCompositionIdByPath(slot.slotValue)
138
+ if (
139
+ compositionId &&
140
+ slot.slotParams &&
141
+ Object.keys(slot.slotParams).length
142
+ ) {
143
+ $store.dispatch({
144
+ type: "compositions/update",
145
+ payload: {
146
+ compositionId,
147
+ compositionParams: slot.slotParams,
148
+ },
149
+ })
150
+ }
151
+ })
85
152
  }
86
153
 
87
154
  export const handleRouteChange = (
@@ -8,6 +8,7 @@
8
8
  "paths": {
9
9
  "@shared/*": ["../backend/src/shared/*"],
10
10
  "@core": ["../.."],
11
+ "@app/*": ["./src/app/*"],
11
12
  "@racletteCore/*": ["./src/core/*"],
12
13
  "@racletteCore": ["./src/core"],
13
14
  "@racletteOrchestrator": ["./src/orchestrator"],