@conduction/nextcloud-vue 2.56.0-beta.21 → 2.56.0-beta.23
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/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +29 -0
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +18 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +9 -6
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +16 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +5 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue2.js +25 -0
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue3.js +3 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue3.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnFlowNode.vue2.js +36 -1
- package/dist/esm/components/CnGraphCanvas/CnFlowNode.vue2.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnFlowNode.vue3.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue2.js +37 -0
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue2.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue3.js +4 -2
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +16 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -6
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue2.js +53 -5
- package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue2.js.map +1 -1
- package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue3.js +1 -1
- package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +133 -0
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +22 -14
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +15 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +3 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/CnWidgetGrid.vue2.js +15 -1
- package/dist/esm/components/CnWidgetGrid/CnWidgetGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/CnWidgetGrid.vue3.js +2 -2
- package/dist/esm/components/CnWidgetGrid/CnWidgetGrid.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/notificationRuleLabel.js +132 -0
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +578 -47
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/package.json +1 -1
- package/scripts/openregister-reliance.js +206 -0
- package/src/components/CnAppRoot/CnAppRoot.md +1 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +29 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +24 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +22 -4
- package/src/components/CnFlowDetail/CnFlowDetail.vue +27 -0
- package/src/components/CnGraphCanvas/CnFlowNode.vue +36 -1
- package/src/components/CnGraphCanvas/CnGraphCanvas.vue +40 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +17 -2
- package/src/components/CnNotificationPreferences/CnNotificationPreferences.vue +54 -6
- package/src/components/CnPageRenderer/CnPageRenderer.vue +147 -6
- package/src/components/CnTabsWidget/CnTabsWidget.vue +17 -2
- package/src/components/CnWidgetGrid/CnWidgetGrid.vue +19 -3
- package/src/index.js +2 -0
- package/src/utils/notificationRuleLabel.js +129 -0
- package/webpack/index.d.ts +16 -0
- package/webpack/index.js +70 -0
|
@@ -136551,7 +136551,7 @@ var script$3W = {
|
|
|
136551
136551
|
},
|
|
136552
136552
|
},
|
|
136553
136553
|
|
|
136554
|
-
emits: ['geo-saved', 'open-integration'],
|
|
136554
|
+
emits: ['geo-saved', 'open-integration', 'select-object'],
|
|
136555
136555
|
|
|
136556
136556
|
setup() {
|
|
136557
136557
|
// Every integration component this host renders goes through the
|
|
@@ -137056,6 +137056,21 @@ var script$3W = {
|
|
|
137056
137056
|
this.$emit('open-integration', integrationId);
|
|
137057
137057
|
},
|
|
137058
137058
|
|
|
137059
|
+
/**
|
|
137060
|
+
* Pass a related-object click up; the page decides where it opens. Heard
|
|
137061
|
+
* from the Related widget itself and from a registry widget that nests a
|
|
137062
|
+
* host of its own (a Tabs widget with a Related tab).
|
|
137063
|
+
*
|
|
137064
|
+
* @param {object} raw The clicked object as the related widget holds it.
|
|
137065
|
+
*/
|
|
137066
|
+
onSelectObject(raw) {
|
|
137067
|
+
/**
|
|
137068
|
+
* @event select-object A related object was clicked in the related widget. Payload is the raw object; its `@self` names the register and schema.
|
|
137069
|
+
* @type {object}
|
|
137070
|
+
*/
|
|
137071
|
+
this.$emit('select-object', raw);
|
|
137072
|
+
},
|
|
137073
|
+
|
|
137059
137074
|
/**
|
|
137060
137075
|
* Delegate to the rendered list widget's own create affordance, so the
|
|
137061
137076
|
* card's Add action and the widget's footer button open the same form.
|
|
@@ -137158,8 +137173,9 @@ function render$3W(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
137158
137173
|
includeGroups: $options.content.groups || [],
|
|
137159
137174
|
hideSingleTabTitle: $options.content.hideSingleTabTitle !== false,
|
|
137160
137175
|
showTotalCount: $options.content.showTotalCount !== false,
|
|
137161
|
-
onOpenIntegration: $options.onOpenIntegration
|
|
137162
|
-
|
|
137176
|
+
onOpenIntegration: $options.onOpenIntegration,
|
|
137177
|
+
onSelectObject: $options.onSelectObject
|
|
137178
|
+
}, null, 8 /* PROPS */, ["title", "bare", "objectType", "objectId", "objectData", "register", "schema", "store", "includeGroups", "hideSingleTabTitle", "showTotalCount", "onOpenIntegration", "onSelectObject"]))
|
|
137163
137179
|
: ($options.isGeo)
|
|
137164
137180
|
? (vue$1.openBlock(), vue$1.createBlock(_component_CnObjectGeoWidget, {
|
|
137165
137181
|
key: 3,
|
|
@@ -137198,7 +137214,7 @@ function render$3W(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
137198
137214
|
class: "cn-detail-page__catalog-card"
|
|
137199
137215
|
}, vue$1.createSlots({
|
|
137200
137216
|
default: vue$1.withCtx(() => [
|
|
137201
|
-
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent($options.renderer), vue$1.mergeProps({ ref: "renderer" }, $options.rendererProps), null, 16 /* FULL_PROPS
|
|
137217
|
+
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent($options.renderer), vue$1.mergeProps({ ref: "renderer" }, $options.rendererProps, { onSelectObject: $options.onSelectObject }), null, 16 /* FULL_PROPS */, ["onSelectObject"]))
|
|
137202
137218
|
]),
|
|
137203
137219
|
_: 2 /* DYNAMIC */
|
|
137204
137220
|
}, [
|
|
@@ -137266,7 +137282,7 @@ function render$3W(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
137266
137282
|
? (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent($options.renderer), vue$1.mergeProps({
|
|
137267
137283
|
key: 8,
|
|
137268
137284
|
ref: "renderer"
|
|
137269
|
-
}, $options.rendererProps), null, 16 /* FULL_PROPS
|
|
137285
|
+
}, $options.rendererProps, { onSelectObject: $options.onSelectObject }), null, 16 /* FULL_PROPS */, ["onSelectObject"]))
|
|
137270
137286
|
: vue$1.createCommentVNode("v-if", true)
|
|
137271
137287
|
], 2 /* CLASS */))
|
|
137272
137288
|
}
|
|
@@ -138248,7 +138264,7 @@ var script$3T = {
|
|
|
138248
138264
|
},
|
|
138249
138265
|
},
|
|
138250
138266
|
|
|
138251
|
-
emits: ['geo-saved', 'open-integration'],
|
|
138267
|
+
emits: ['geo-saved', 'open-integration', 'select-object'],
|
|
138252
138268
|
|
|
138253
138269
|
data() {
|
|
138254
138270
|
return {
|
|
@@ -138421,6 +138437,20 @@ var script$3T = {
|
|
|
138421
138437
|
this.$emit('open-integration', integrationId);
|
|
138422
138438
|
},
|
|
138423
138439
|
|
|
138440
|
+
/**
|
|
138441
|
+
* Re-emit a related child's object click; the page opens the object.
|
|
138442
|
+
*
|
|
138443
|
+
* @param {object} raw The clicked object.
|
|
138444
|
+
* @return {void}
|
|
138445
|
+
*/
|
|
138446
|
+
onSelectObject(raw) {
|
|
138447
|
+
/**
|
|
138448
|
+
* @event select-object Re-emitted from a related child whose object row was clicked. Payload is the raw object.
|
|
138449
|
+
* @type {object}
|
|
138450
|
+
*/
|
|
138451
|
+
this.$emit('select-object', raw);
|
|
138452
|
+
},
|
|
138453
|
+
|
|
138424
138454
|
/**
|
|
138425
138455
|
* Follow the strip's own selection, so keyboard navigation moves the
|
|
138426
138456
|
* hoisted Actions menu too.
|
|
@@ -138547,8 +138577,9 @@ function render$3T(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
138547
138577
|
integrationContext: $props.integrationContext,
|
|
138548
138578
|
cnRegistry: $props.cnRegistry,
|
|
138549
138579
|
onGeoSaved: $options.onGeoSaved,
|
|
138550
|
-
onOpenIntegration: $options.onOpenIntegration
|
|
138551
|
-
|
|
138580
|
+
onOpenIntegration: $options.onOpenIntegration,
|
|
138581
|
+
onSelectObject: $options.onSelectObject
|
|
138582
|
+
}, null, 8 /* PROPS */, ["widget", "objectId", "object", "objectType", "schemaObject", "register", "schema", "store", "surface", "integrationContext", "cnRegistry", "onGeoSaved", "onOpenIntegration", "onSelectObject"]))
|
|
138552
138583
|
: (vue$1.openBlock(), vue$1.createBlock(_component_NcEmptyContent, {
|
|
138553
138584
|
key: 1,
|
|
138554
138585
|
name: $options.missingLabel(entry)
|
|
@@ -231610,6 +231641,19 @@ var script$22 = {
|
|
|
231610
231641
|
* edges, its undo stack, its persistence — so nothing is removed here.
|
|
231611
231642
|
*/
|
|
231612
231643
|
'remove',
|
|
231644
|
+
|
|
231645
|
+
/**
|
|
231646
|
+
* @event activate Enter or Space on the focused node: open this step.
|
|
231647
|
+
* Carries the node id. The pointer equivalent is a double-click.
|
|
231648
|
+
*/
|
|
231649
|
+
'activate',
|
|
231650
|
+
|
|
231651
|
+
/**
|
|
231652
|
+
* @event menu Shift+F10 or the menu key on the focused node: show this
|
|
231653
|
+
* step's actions. Carries `{ id, clientX, clientY }`, a point at the
|
|
231654
|
+
* node, so a host positions the menu as it would for a click.
|
|
231655
|
+
*/
|
|
231656
|
+
'menu',
|
|
231613
231657
|
],
|
|
231614
231658
|
|
|
231615
231659
|
setup() {
|
|
@@ -231864,7 +231908,8 @@ var script$22 = {
|
|
|
231864
231908
|
* The keyboard contract.
|
|
231865
231909
|
*
|
|
231866
231910
|
* Arrow keys move; `c` arms an exit and, on a second node, completes the
|
|
231867
|
-
* connection; `Escape` cancels.
|
|
231911
|
+
* connection; `Escape` cancels. Enter or Space opens the step, Shift+F10
|
|
231912
|
+
* or the menu key opens its actions. Repeated `c` on the SAME node steps
|
|
231868
231913
|
* through its exits rather than re-arming the first — the behaviour a
|
|
231869
231914
|
* mouse gets for free by pointing.
|
|
231870
231915
|
*
|
|
@@ -231913,6 +231958,27 @@ var script$22 = {
|
|
|
231913
231958
|
return
|
|
231914
231959
|
}
|
|
231915
231960
|
|
|
231961
|
+
// Opening the step, and its menu, from the keyboard. Only for a key
|
|
231962
|
+
// pressed ON the node: a port inside it is a control of its own.
|
|
231963
|
+
if (event.target === event.currentTarget) {
|
|
231964
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
231965
|
+
event.preventDefault();
|
|
231966
|
+
this.$emit('activate', this.id);
|
|
231967
|
+
return
|
|
231968
|
+
}
|
|
231969
|
+
|
|
231970
|
+
if ((event.key === 'F10' && event.shiftKey === true) || event.key === 'ContextMenu') {
|
|
231971
|
+
event.preventDefault();
|
|
231972
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
231973
|
+
this.$emit('menu', {
|
|
231974
|
+
id: this.id,
|
|
231975
|
+
clientX: rect.left + rect.width / 2,
|
|
231976
|
+
clientY: rect.bottom,
|
|
231977
|
+
});
|
|
231978
|
+
return
|
|
231979
|
+
}
|
|
231980
|
+
}
|
|
231981
|
+
|
|
231916
231982
|
// Delete and Backspace both, because which one removes a thing is a
|
|
231917
231983
|
// platform habit rather than a preference: Backspace is the delete
|
|
231918
231984
|
// key on a Mac keyboard, and a user who learned one does not think
|
|
@@ -232270,6 +232336,8 @@ var script$21 = {
|
|
|
232270
232336
|
'canvas-drop',
|
|
232271
232337
|
|
|
232272
232338
|
'node-remove',
|
|
232339
|
+
'node-activate',
|
|
232340
|
+
'node-menu',
|
|
232273
232341
|
|
|
232274
232342
|
/**
|
|
232275
232343
|
* The canvas zoom changed, by any route — a control here, the host's own
|
|
@@ -232419,6 +232487,41 @@ var script$21 = {
|
|
|
232419
232487
|
return port === 'in' ? null : port
|
|
232420
232488
|
},
|
|
232421
232489
|
|
|
232490
|
+
/**
|
|
232491
|
+
* Forward Enter or Space on a node: the keyboard double-click.
|
|
232492
|
+
*
|
|
232493
|
+
* @param {string} id The node id.
|
|
232494
|
+
* @return {void}
|
|
232495
|
+
*/
|
|
232496
|
+
onNodeActivate(id) {
|
|
232497
|
+
if (this.readOnly === true) {
|
|
232498
|
+
return
|
|
232499
|
+
}
|
|
232500
|
+
|
|
232501
|
+
/**
|
|
232502
|
+
* @event node-activate Enter or Space on a node. Payload is the id.
|
|
232503
|
+
*/
|
|
232504
|
+
this.$emit('node-activate', id);
|
|
232505
|
+
},
|
|
232506
|
+
|
|
232507
|
+
/**
|
|
232508
|
+
* Forward Shift+F10 or the menu key on a node: the keyboard click menu.
|
|
232509
|
+
*
|
|
232510
|
+
* @param {object} request `{ id, clientX, clientY }`, a point at the node.
|
|
232511
|
+
* @return {void}
|
|
232512
|
+
*/
|
|
232513
|
+
onNodeMenu(request) {
|
|
232514
|
+
if (this.readOnly === true) {
|
|
232515
|
+
return
|
|
232516
|
+
}
|
|
232517
|
+
|
|
232518
|
+
/**
|
|
232519
|
+
* @event node-menu Shift+F10 or the menu key on a node. Payload is
|
|
232520
|
+
* `{ id, clientX, clientY }`.
|
|
232521
|
+
*/
|
|
232522
|
+
this.$emit('node-menu', request);
|
|
232523
|
+
},
|
|
232524
|
+
|
|
232422
232525
|
/**
|
|
232423
232526
|
* Forward a node click.
|
|
232424
232527
|
*
|
|
@@ -232583,13 +232686,15 @@ function render$21(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
232583
232686
|
vue$1.createVNode(_component_CnFlowNode, vue$1.mergeProps(nodeProps, {
|
|
232584
232687
|
deletable: !$props.readOnly,
|
|
232585
232688
|
onConnect: $options.onConnect,
|
|
232586
|
-
onRemove: $options.onNodeRemove
|
|
232689
|
+
onRemove: $options.onNodeRemove,
|
|
232690
|
+
onActivate: $options.onNodeActivate,
|
|
232691
|
+
onMenu: $options.onNodeMenu
|
|
232587
232692
|
}), {
|
|
232588
232693
|
default: vue$1.withCtx((slotProps) => [
|
|
232589
232694
|
vue$1.renderSlot(_ctx.$slots, "node", vue$1.normalizeProps(vue$1.guardReactiveProps(slotProps)))
|
|
232590
232695
|
]),
|
|
232591
232696
|
_: 3 /* FORWARDED */
|
|
232592
|
-
}, 16 /* FULL_PROPS */, ["deletable", "onConnect", "onRemove"])
|
|
232697
|
+
}, 16 /* FULL_PROPS */, ["deletable", "onConnect", "onRemove", "onActivate", "onMenu"])
|
|
232593
232698
|
]),
|
|
232594
232699
|
"edge-default": vue$1.withCtx((edgeProps) => [
|
|
232595
232700
|
vue$1.createVNode(_component_CnFlowEdge, vue$1.mergeProps(edgeProps, {
|
|
@@ -234359,6 +234464,31 @@ var script$1$ = {
|
|
|
234359
234464
|
this.openNodeMenu({ item: id, event: mouse });
|
|
234360
234465
|
},
|
|
234361
234466
|
|
|
234467
|
+
/**
|
|
234468
|
+
* Enter or Space on a step: open its editor, as a double-click does.
|
|
234469
|
+
*
|
|
234470
|
+
* @param {string} id The step id.
|
|
234471
|
+
* @return {void}
|
|
234472
|
+
*/
|
|
234473
|
+
onNodeActivate(id) {
|
|
234474
|
+
this.store.selectedNodeId = id;
|
|
234475
|
+
this.store.editingNodeId = id;
|
|
234476
|
+
},
|
|
234477
|
+
|
|
234478
|
+
/**
|
|
234479
|
+
* Shift+F10 or the menu key on a step: its menu, at the step.
|
|
234480
|
+
*
|
|
234481
|
+
* @param {object} request The node's request.
|
|
234482
|
+
* @param {string} request.id The step id.
|
|
234483
|
+
* @param {number} request.clientX Where to place the menu.
|
|
234484
|
+
* @param {number} request.clientY Where to place the menu.
|
|
234485
|
+
* @return {void}
|
|
234486
|
+
*/
|
|
234487
|
+
onNodeMenu({ id, clientX, clientY }) {
|
|
234488
|
+
this.store.selectedNodeId = id;
|
|
234489
|
+
this.openNodeMenu({ item: id, event: { clientX, clientY } });
|
|
234490
|
+
},
|
|
234491
|
+
|
|
234362
234492
|
/**
|
|
234363
234493
|
* Place a palette node where it was dropped.
|
|
234364
234494
|
*
|
|
@@ -234973,6 +235103,8 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
234973
235103
|
maxZoom: $data.maxZoom,
|
|
234974
235104
|
onZoomChange: _cache[8] || (_cache[8] = $event => ($data.zoom = $event)),
|
|
234975
235105
|
onNodeSelect: $options.onNodeSelect,
|
|
235106
|
+
onNodeActivate: $options.onNodeActivate,
|
|
235107
|
+
onNodeMenu: $options.onNodeMenu,
|
|
234976
235108
|
onEdgeSelect: $options.onEdgeSelect,
|
|
234977
235109
|
onEdgeLabelClick: $options.onEdgeLabelClick,
|
|
234978
235110
|
onEdgeLabelContext: $options.onEdgeLabelContext,
|
|
@@ -235018,7 +235150,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
235018
235150
|
: vue$1.createCommentVNode("v-if", true)
|
|
235019
235151
|
]),
|
|
235020
235152
|
_: 1 /* STABLE */
|
|
235021
|
-
}, 8 /* PROPS */, ["nodes", "edges", "minZoom", "maxZoom", "onNodeSelect", "onEdgeSelect", "onEdgeLabelClick", "onEdgeLabelContext", "onEdgeLabelMove", "onCanvasClick", "onNodesChange", "onCanvasDrop"]),
|
|
235153
|
+
}, 8 /* PROPS */, ["nodes", "edges", "minZoom", "maxZoom", "onNodeSelect", "onNodeActivate", "onNodeMenu", "onEdgeSelect", "onEdgeLabelClick", "onEdgeLabelContext", "onEdgeLabelMove", "onCanvasClick", "onNodesChange", "onCanvasDrop"]),
|
|
235022
235154
|
vue$1.createVNode(_component_CnContextMenu, {
|
|
235023
235155
|
open: $setup.nodeMenuOpen,
|
|
235024
235156
|
"onUpdate:open": _cache[11] || (_cache[11] = $event => (($setup.nodeMenuOpen) = $event)),
|
|
@@ -248243,7 +248375,7 @@ function namedFilterToQuery(values) {
|
|
|
248243
248375
|
* @event {object} row-aux-click — Table row or card middle-clicked. Payload: (row, event) — the row object and the native auxclick event
|
|
248244
248376
|
* @event {{ key: string, order: string }} sort — Column sort changed
|
|
248245
248377
|
* @event {number} page-changed — Pagination page changed
|
|
248246
|
-
* @event {number} page-size-changed — Pagination page size changed
|
|
248378
|
+
* @event {number} page-size-changed — Pagination page size changed. In self-fetch mode the list refetches page 1 at the new size first.
|
|
248247
248379
|
* @event {string[]} select — Selection changed. Payload: array of selected IDs
|
|
248248
248380
|
* @event {object} action — Row action triggered. Payload: { action, row }
|
|
248249
248381
|
* @event {{ action: string, id: string, selectedIds: Array, count: number }} bulk-action — A declarative bulk action was triggered from the selection strip. The SELECTION travels with it: an action that has to go and find out what was selected is one re-render away from acting on a different set than the user saw highlighted.
|
|
@@ -252668,6 +252800,21 @@ var script$1A = {
|
|
|
252668
252800
|
this.$emit('page-changed', page);
|
|
252669
252801
|
},
|
|
252670
252802
|
|
|
252803
|
+
/**
|
|
252804
|
+
* Page-size pick from CnPagination. In self-fetch mode the list refetches
|
|
252805
|
+
* page 1 at the new size — the event alone left the select changing and
|
|
252806
|
+
* the table not, since a manifest page has no host listening.
|
|
252807
|
+
*
|
|
252808
|
+
* @param {number} size Requested page size.
|
|
252809
|
+
* @return {void}
|
|
252810
|
+
*/
|
|
252811
|
+
onPageSizeEvent(size) {
|
|
252812
|
+
if (this.isSelfFetchMode && typeof this.list.onPageSizeChange === 'function') {
|
|
252813
|
+
this.list.onPageSizeChange(size);
|
|
252814
|
+
}
|
|
252815
|
+
this.$emit('page-size-changed', size);
|
|
252816
|
+
},
|
|
252817
|
+
|
|
252671
252818
|
/**
|
|
252672
252819
|
* @param {{key: string, values: Array}} payload Facet-filter change from the sidebar.
|
|
252673
252820
|
* @return {void}
|
|
@@ -254275,7 +254422,7 @@ function render$1A(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
254275
254422
|
return (vue$1.openBlock(), vue$1.createElementBlock("div", {
|
|
254276
254423
|
class: "cn-index-page",
|
|
254277
254424
|
"data-testid": "cn-index-page",
|
|
254278
|
-
onKeydown: _cache[
|
|
254425
|
+
onKeydown: _cache[25] || (_cache[25] = (...args) => ($options.onListKeydown && $options.onListKeydown(...args)))
|
|
254279
254426
|
}, [
|
|
254280
254427
|
vue$1.renderSlot(_ctx.$slots, "header", {
|
|
254281
254428
|
title: $props.title,
|
|
@@ -254765,7 +254912,7 @@ function render$1A(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
254765
254912
|
'cn-index-page__main--table': $data.currentViewMode === 'table',
|
|
254766
254913
|
'cn-index-page__main--split': $options.splitLayout === 'split',
|
|
254767
254914
|
}]),
|
|
254768
|
-
onScroll: _cache[
|
|
254915
|
+
onScroll: _cache[22] || (_cache[22] = (...args) => ($options.onListScroll && $options.onListScroll(...args)))
|
|
254769
254916
|
}, [
|
|
254770
254917
|
vue$1.renderSlot(_ctx.$slots, "before-collection"),
|
|
254771
254918
|
($options.showInitialLoader)
|
|
@@ -255134,8 +255281,8 @@ function render$1A(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
255134
255281
|
currentPageSize: $options.effectivePagination.limit || 20,
|
|
255135
255282
|
class: "cn-index-page__pagination",
|
|
255136
255283
|
onPageChanged: $options.onPageEvent,
|
|
255137
|
-
onPageSizeChanged:
|
|
255138
|
-
}, null, 8 /* PROPS */, ["currentPage", "totalPages", "totalItems", "currentPageSize", "onPageChanged"]))
|
|
255284
|
+
onPageSizeChanged: $options.onPageSizeEvent
|
|
255285
|
+
}, null, 8 /* PROPS */, ["currentPage", "totalPages", "totalItems", "currentPageSize", "onPageChanged", "onPageSizeChanged"]))
|
|
255139
255286
|
: vue$1.createCommentVNode("v-if", true)
|
|
255140
255287
|
], 34 /* CLASS, NEED_HYDRATION */), [
|
|
255141
255288
|
[vue$1.vShow, $options.splitLayout !== 'detail']
|
|
@@ -255155,7 +255302,7 @@ function render$1A(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
255155
255302
|
href: $options.splitCloseHref,
|
|
255156
255303
|
title: $options.splitCloseTitle,
|
|
255157
255304
|
"data-testid": "cn-index-page-split-close",
|
|
255158
|
-
onClick: _cache[
|
|
255305
|
+
onClick: _cache[23] || (_cache[23] = vue$1.withModifiers((...args) => ($options.closeSplitPane && $options.closeSplitPane(...args)), ["prevent"]))
|
|
255159
255306
|
}, [
|
|
255160
255307
|
vue$1.createVNode(_component_Close, { size: 16 }),
|
|
255161
255308
|
vue$1.createElementVNode("span", null, vue$1.toDisplayString($options.splitCloseTitle), 1 /* TEXT */)
|
|
@@ -255185,7 +255332,7 @@ function render$1A(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
255185
255332
|
facetData: $options.effectiveFacetData,
|
|
255186
255333
|
showMetadata: $options.resolvedSidebar.showMetadata !== false
|
|
255187
255334
|
}, $options.sidebarSearchProps, {
|
|
255188
|
-
"onUpdate:open": _cache[
|
|
255335
|
+
"onUpdate:open": _cache[24] || (_cache[24] = $event => ($data.sidebarOpen = $event)),
|
|
255189
255336
|
onSearch: $options.onSearchEvent,
|
|
255190
255337
|
onColumnsChange: $options.onColumnsEvent,
|
|
255191
255338
|
onFilterChange: $options.onFilterEvent,
|
|
@@ -268485,6 +268632,7 @@ var script$1c = {
|
|
|
268485
268632
|
'layout-change',
|
|
268486
268633
|
'open-integration',
|
|
268487
268634
|
'refresh',
|
|
268635
|
+
'related-object-click',
|
|
268488
268636
|
'related-row-click',
|
|
268489
268637
|
'relation-linked',
|
|
268490
268638
|
'request-feature',
|
|
@@ -270466,6 +270614,23 @@ var script$1c = {
|
|
|
270466
270614
|
this.$emit('open-integration', integrationId);
|
|
270467
270615
|
},
|
|
270468
270616
|
|
|
270617
|
+
/**
|
|
270618
|
+
* Re-emit a click on an object in the Related widget. The page does not
|
|
270619
|
+
* know the routes; the host (CnPageRenderer on a manifest page) opens
|
|
270620
|
+
* the object's own detail page.
|
|
270621
|
+
*
|
|
270622
|
+
* @param {object} raw The clicked object; `@self` names its register and schema.
|
|
270623
|
+
*/
|
|
270624
|
+
onRelatedObjectSelect(raw) {
|
|
270625
|
+
/**
|
|
270626
|
+
* @event related-object-click An object in the Related widget was
|
|
270627
|
+
* clicked. Payload is the raw object; `@self` carries its register,
|
|
270628
|
+
* schema and id.
|
|
270629
|
+
* @type {object}
|
|
270630
|
+
*/
|
|
270631
|
+
this.$emit('related-object-click', raw);
|
|
270632
|
+
},
|
|
270633
|
+
|
|
270469
270634
|
/**
|
|
270470
270635
|
* Re-fetch the object after the geo widget persists `@self.geo` (so the
|
|
270471
270636
|
* rest of the page — metadata, other widgets — reflects the new value),
|
|
@@ -272276,8 +272441,9 @@ function render$1c(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
272276
272441
|
cnRegistry: $options.cnRegistry,
|
|
272277
272442
|
availableWidgets: _ctx.widgets,
|
|
272278
272443
|
onGeoSaved: $options.onGeoSaved,
|
|
272279
|
-
onOpenIntegration: $options.onAutoBodyOpenIntegration
|
|
272280
|
-
|
|
272444
|
+
onOpenIntegration: $options.onAutoBodyOpenIntegration,
|
|
272445
|
+
onSelectObject: $options.onRelatedObjectSelect
|
|
272446
|
+
}, null, 8 /* PROPS */, ["widget", "objectId", "object", "objectType", "schemaObject", "register", "schema", "store", "surface", "integrationContext", "hideEmpty", "cnRegistry", "availableWidgets", "onGeoSaved", "onOpenIntegration", "onSelectObject"])
|
|
272281
272447
|
])
|
|
272282
272448
|
]))
|
|
272283
272449
|
: vue$1.createCommentVNode("v-if", true)
|
|
@@ -272501,8 +272667,9 @@ function render$1c(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
272501
272667
|
cnRegistry: $options.cnRegistry,
|
|
272502
272668
|
availableWidgets: $options.bodyGridWidgets,
|
|
272503
272669
|
onGeoSaved: $options.onGeoSaved,
|
|
272504
|
-
onOpenIntegration: $options.onAutoBodyOpenIntegration
|
|
272505
|
-
|
|
272670
|
+
onOpenIntegration: $options.onAutoBodyOpenIntegration,
|
|
272671
|
+
onSelectObject: $options.onRelatedObjectSelect
|
|
272672
|
+
}, null, 8 /* PROPS */, ["widget", "showCardTitle", "showActions", "objectId", "object", "objectType", "schemaObject", "register", "schema", "store", "surface", "integrationContext", "hideEmpty", "cnRegistry", "availableWidgets", "onGeoSaved", "onOpenIntegration", "onSelectObject"])
|
|
272506
272673
|
])
|
|
272507
272674
|
], 10 /* CLASS, PROPS */, _hoisted_19$8)
|
|
272508
272675
|
]),
|
|
@@ -272683,8 +272850,9 @@ function render$1c(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
272683
272850
|
cnRegistry: $options.cnRegistry,
|
|
272684
272851
|
availableWidgets: _ctx.widgets,
|
|
272685
272852
|
onGeoSaved: $options.onGeoSaved,
|
|
272686
|
-
onOpenIntegration: $options.onAutoBodyOpenIntegration
|
|
272687
|
-
|
|
272853
|
+
onOpenIntegration: $options.onAutoBodyOpenIntegration,
|
|
272854
|
+
onSelectObject: $options.onRelatedObjectSelect
|
|
272855
|
+
}, null, 8 /* PROPS */, ["widget", "showActions", "objectId", "object", "objectType", "schemaObject", "register", "schema", "store", "surface", "integrationContext", "hideEmpty", "cnRegistry", "availableWidgets", "onGeoSaved", "onOpenIntegration", "onSelectObject"])
|
|
272688
272856
|
])
|
|
272689
272857
|
], 8 /* PROPS */, _hoisted_34$1))
|
|
272690
272858
|
}), 128 /* KEYED_FRAGMENT */))
|
|
@@ -295213,7 +295381,7 @@ var script$s = {
|
|
|
295213
295381
|
},
|
|
295214
295382
|
},
|
|
295215
295383
|
|
|
295216
|
-
emits: ['layout-change'],
|
|
295384
|
+
emits: ['layout-change', 'select-object'],
|
|
295217
295385
|
|
|
295218
295386
|
data() {
|
|
295219
295387
|
return {
|
|
@@ -295423,6 +295591,20 @@ var script$s = {
|
|
|
295423
295591
|
},
|
|
295424
295592
|
|
|
295425
295593
|
methods: {
|
|
295594
|
+
/**
|
|
295595
|
+
* Pass a related widget's object click up; the page opens the object.
|
|
295596
|
+
*
|
|
295597
|
+
* @param {object} raw The clicked object.
|
|
295598
|
+
* @return {void}
|
|
295599
|
+
*/
|
|
295600
|
+
onSelectObject(raw) {
|
|
295601
|
+
/**
|
|
295602
|
+
* @event select-object An object row in a related widget of this grid was clicked. Payload is the raw object.
|
|
295603
|
+
* @type {object}
|
|
295604
|
+
*/
|
|
295605
|
+
this.$emit('select-object', raw);
|
|
295606
|
+
},
|
|
295607
|
+
|
|
295426
295608
|
// Expose the shared grid helpers to the template.
|
|
295427
295609
|
cnGridCellStyle,
|
|
295428
295610
|
hasGridRow,
|
|
@@ -295517,7 +295699,7 @@ function render$s(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
295517
295699
|
"gs-h": widget.gridHeight
|
|
295518
295700
|
}, [
|
|
295519
295701
|
vue$1.createElementVNode("div", _hoisted_4$v, [
|
|
295520
|
-
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(widget.component), vue$1.mergeProps({ ref_for: true }, widget.props), null, 16 /* FULL_PROPS
|
|
295702
|
+
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(widget.component), vue$1.mergeProps({ ref_for: true }, widget.props, { onSelectObject: $options.onSelectObject }), null, 16 /* FULL_PROPS */, ["onSelectObject"]))
|
|
295521
295703
|
])
|
|
295522
295704
|
], 8 /* PROPS */, _hoisted_3$w))
|
|
295523
295705
|
}), 128 /* KEYED_FRAGMENT */))
|
|
@@ -295535,7 +295717,7 @@ function render$s(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
295535
295717
|
class: vue$1.normalizeClass(["cn-grid__item", { 'cn-grid__item--row': $options.hasGridRow(widget) }]),
|
|
295536
295718
|
style: vue$1.normalizeStyle($options.cnGridCellStyle(widget, $options.gridColumns))
|
|
295537
295719
|
}, [
|
|
295538
|
-
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(widget.component), vue$1.mergeProps({ ref_for: true }, widget.props), null, 16 /* FULL_PROPS
|
|
295720
|
+
(vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(widget.component), vue$1.mergeProps({ ref_for: true }, widget.props, { onSelectObject: $options.onSelectObject }), null, 16 /* FULL_PROPS */, ["onSelectObject"]))
|
|
295539
295721
|
], 6 /* CLASS, STYLE */))
|
|
295540
295722
|
}), 128 /* KEYED_FRAGMENT */))
|
|
295541
295723
|
], 14 /* CLASS, STYLE, PROPS */, _hoisted_5$j))
|
|
@@ -296038,6 +296220,53 @@ const defaultPageTypes = {
|
|
|
296038
296220
|
'flow-detail': vue$1.defineAsyncComponent(() => Promise.resolve().then(function () { return CnFlowEditorPage; }).then((m) => m.default)),
|
|
296039
296221
|
};
|
|
296040
296222
|
|
|
296223
|
+
/**
|
|
296224
|
+
* OpenRegister registers and schemas already described by id, so a click on
|
|
296225
|
+
* a related object costs at most one request per register or schema for as
|
|
296226
|
+
* long as this module stays loaded (the SPA's lifetime, across page
|
|
296227
|
+
* changes). Keyed by `${kind}:${id}`; a failed lookup is dropped again so the
|
|
296228
|
+
* next click retries it.
|
|
296229
|
+
*
|
|
296230
|
+
* @type {Map<string, Promise<{ id: string, slug: string }>>}
|
|
296231
|
+
*/
|
|
296232
|
+
const entityDescriptorCache = new Map();
|
|
296233
|
+
|
|
296234
|
+
/**
|
|
296235
|
+
* Describe an OpenRegister register or schema (`{ id, slug }`) by its id or
|
|
296236
|
+
* slug, or null when it cannot be fetched. An object's `@self` names its
|
|
296237
|
+
* register and schema by id while a manifest may name them by slug; this is
|
|
296238
|
+
* the bridge. Lazy-imports axios and the router helper like the widgets do.
|
|
296239
|
+
*
|
|
296240
|
+
* @param {'registers'|'schemas'} kind The collection to ask.
|
|
296241
|
+
* @param {string|number} idOrSlug The id (or slug) to describe.
|
|
296242
|
+
* @return {Promise<{ id: string, slug: string }|null>} The descriptor.
|
|
296243
|
+
*/
|
|
296244
|
+
function describeOpenRegisterEntity(kind, idOrSlug) {
|
|
296245
|
+
const key = `${kind}:${idOrSlug}`;
|
|
296246
|
+
if (!entityDescriptorCache.has(key)) {
|
|
296247
|
+
entityDescriptorCache.set(key, (async () => {
|
|
296248
|
+
try {
|
|
296249
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([
|
|
296250
|
+
import('@nextcloud/axios'),
|
|
296251
|
+
import('@nextcloud/router'),
|
|
296252
|
+
]);
|
|
296253
|
+
const url = generateUrl('/apps/openregister/api/{kind}/{id}', { kind, id: String(idOrSlug) });
|
|
296254
|
+
const res = await axios.get(url);
|
|
296255
|
+
const data = res && res.data;
|
|
296256
|
+
if (!data || typeof data !== 'object') {
|
|
296257
|
+
throw new Error(`no descriptor for ${key}`)
|
|
296258
|
+
}
|
|
296259
|
+
return { id: String(data.id ?? idOrSlug), slug: String(data.slug ?? '') }
|
|
296260
|
+
} catch (error) {
|
|
296261
|
+
// One failed request must not pin every later click to nowhere.
|
|
296262
|
+
entityDescriptorCache.delete(key);
|
|
296263
|
+
throw error
|
|
296264
|
+
}
|
|
296265
|
+
})());
|
|
296266
|
+
}
|
|
296267
|
+
return entityDescriptorCache.get(key).catch(() => null)
|
|
296268
|
+
}
|
|
296269
|
+
|
|
296041
296270
|
/** Recognised fixed slot names for v2 manifests. */
|
|
296042
296271
|
const KNOWN_SLOTS = new Set(['body', 'sidebar', 'header-actions', 'footer', 'modal']);
|
|
296043
296272
|
|
|
@@ -297565,6 +297794,92 @@ var script$r = {
|
|
|
297565
297794
|
})
|
|
297566
297795
|
},
|
|
297567
297796
|
|
|
297797
|
+
/**
|
|
297798
|
+
* Open the detail page of an object clicked in a Related widget —
|
|
297799
|
+
* reaching here as `CnDetailPage`'s `related-object-click`, or as
|
|
297800
|
+
* `CnWidgetGrid`'s `select-object` on a v2 widget-grid page. Nothing
|
|
297801
|
+
* listened to either, so the rows looked like links and went nowhere.
|
|
297802
|
+
*
|
|
297803
|
+
* @param {object} raw The clicked object as the Related widget holds it.
|
|
297804
|
+
* @return {Promise<void>}
|
|
297805
|
+
*/
|
|
297806
|
+
async onRelatedObjectOpen(raw) {
|
|
297807
|
+
const target = await this.relatedObjectTarget(raw);
|
|
297808
|
+
if (target) {
|
|
297809
|
+
this.$router.push(target).catch(() => {});
|
|
297810
|
+
}
|
|
297811
|
+
},
|
|
297812
|
+
|
|
297813
|
+
/**
|
|
297814
|
+
* Where a related object opens: the first `type: 'detail'` page bound
|
|
297815
|
+
* to the object's register and schema, with the id in whichever param
|
|
297816
|
+
* that page's route declares. The object's `@self` names register and
|
|
297817
|
+
* schema by id and a manifest may name them by slug, so a miss on the
|
|
297818
|
+
* literal pair describes both (one request each, cached) and matches
|
|
297819
|
+
* again. Null, silently, when there is no router or the object carries
|
|
297820
|
+
* no register, schema or id (nothing to open); null with a warning,
|
|
297821
|
+
* like a dead row click, when no detail page matches or the router
|
|
297822
|
+
* lacks the route.
|
|
297823
|
+
*
|
|
297824
|
+
* @param {object} raw The clicked object.
|
|
297825
|
+
* @return {Promise<object|null>} The router location, or null.
|
|
297826
|
+
*/
|
|
297827
|
+
async relatedObjectTarget(raw) {
|
|
297828
|
+
const router = this.$router;
|
|
297829
|
+
if (!router || !raw || typeof raw !== 'object') {
|
|
297830
|
+
return null
|
|
297831
|
+
}
|
|
297832
|
+
const self = raw['@self'] || {};
|
|
297833
|
+
const id = raw.id ?? self.id ?? self.uuid ?? raw.uuid;
|
|
297834
|
+
const register = self.register ?? raw.register;
|
|
297835
|
+
const schema = self.schema ?? raw.schema;
|
|
297836
|
+
const missing = (v) => v === undefined || v === null || v === '';
|
|
297837
|
+
if (missing(id) || missing(register) || missing(schema)) {
|
|
297838
|
+
return null
|
|
297839
|
+
}
|
|
297840
|
+
let page = this.detailPageFor([String(register)], [String(schema)]);
|
|
297841
|
+
if (!page) {
|
|
297842
|
+
const [reg, sch] = await Promise.all([
|
|
297843
|
+
describeOpenRegisterEntity('registers', register),
|
|
297844
|
+
describeOpenRegisterEntity('schemas', schema),
|
|
297845
|
+
]);
|
|
297846
|
+
page = this.detailPageFor(
|
|
297847
|
+
[String(register), reg?.id, reg?.slug].filter(Boolean),
|
|
297848
|
+
[String(schema), sch?.id, sch?.slug].filter(Boolean),
|
|
297849
|
+
);
|
|
297850
|
+
}
|
|
297851
|
+
if (!page) {
|
|
297852
|
+
// eslint-disable-next-line no-console
|
|
297853
|
+
console.warn(`[CnPageRenderer] No detail page is bound to register "${register}" and schema "${schema}", so related object "${id}" opens nowhere.`);
|
|
297854
|
+
return null
|
|
297855
|
+
}
|
|
297856
|
+
if (this.routeNameIsKnown(page.id) === false) {
|
|
297857
|
+
// eslint-disable-next-line no-console
|
|
297858
|
+
console.warn(`[CnPageRenderer] Detail page "${page.id}" is not a route the router has, so related object "${id}" opens nowhere.`);
|
|
297859
|
+
return null
|
|
297860
|
+
}
|
|
297861
|
+
const path = page.route ?? routePathFor(router, page.id);
|
|
297862
|
+
return { name: page.id, params: buildRouteParams(path, id, this.$route?.params) }
|
|
297863
|
+
},
|
|
297864
|
+
|
|
297865
|
+
/**
|
|
297866
|
+
* The first `type: 'detail'` page whose register and schema are both
|
|
297867
|
+
* among the given names (ids or slugs, compared as strings).
|
|
297868
|
+
*
|
|
297869
|
+
* @param {Array<string>} registerNames Names the register goes by.
|
|
297870
|
+
* @param {Array<string>} schemaNames Names the schema goes by.
|
|
297871
|
+
* @return {object|null} The page, or null.
|
|
297872
|
+
*/
|
|
297873
|
+
detailPageFor(registerNames, schemaNames) {
|
|
297874
|
+
for (const page of this.detailPageByRegisterSchema.values()) {
|
|
297875
|
+
const cfg = page.config || {};
|
|
297876
|
+
if (registerNames.includes(String(cfg.register)) && schemaNames.includes(String(cfg.schema))) {
|
|
297877
|
+
return page
|
|
297878
|
+
}
|
|
297879
|
+
}
|
|
297880
|
+
return null
|
|
297881
|
+
},
|
|
297882
|
+
|
|
297568
297883
|
/**
|
|
297569
297884
|
* Whether the router has a route by this name.
|
|
297570
297885
|
*
|
|
@@ -297974,14 +298289,16 @@ function render$r(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
297974
298289
|
key: 0,
|
|
297975
298290
|
widgets: $options.widgetsBySlot.get('body'),
|
|
297976
298291
|
editable: $options.bodyEditable,
|
|
297977
|
-
slotName: "body"
|
|
297978
|
-
|
|
298292
|
+
slotName: "body",
|
|
298293
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298294
|
+
}, null, 8 /* PROPS */, ["widgets", "editable", "onSelectObject"]))
|
|
297979
298295
|
: ($options.resolvedComponent)
|
|
297980
298296
|
? (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent($options.resolvedComponent), vue$1.mergeProps({ key: $options.pageRenderKey }, { ..._ctx.$attrs, ...$options.resolvedProps }, {
|
|
297981
298297
|
onView: $options.onRowOpen,
|
|
297982
298298
|
onRowClick: $options.onRowOpen,
|
|
297983
298299
|
onRowAuxClick: $options.onRowOpen,
|
|
297984
298300
|
onEditOpen: $options.onRowOpen,
|
|
298301
|
+
onRelatedObjectClick: $options.onRelatedObjectOpen,
|
|
297985
298302
|
onConfigure: _cache[0] || (_cache[0] = $event => ($data.showConfigModal = true))
|
|
297986
298303
|
}), vue$1.createSlots({ _: 2 /* DYNAMIC */ }, [
|
|
297987
298304
|
vue$1.renderList($options.resolvedSlotEntries, (entry) => {
|
|
@@ -298017,42 +298334,47 @@ function render$r(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
298017
298334
|
key: "0"
|
|
298018
298335
|
}
|
|
298019
298336
|
: undefined
|
|
298020
|
-
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["onView", "onRowClick", "onRowAuxClick", "onEditOpen"]))
|
|
298337
|
+
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["onView", "onRowClick", "onRowAuxClick", "onEditOpen", "onRelatedObjectClick"]))
|
|
298021
298338
|
: vue$1.createCommentVNode("v-if", true),
|
|
298022
298339
|
($options.widgetsBySlot.has('header-actions'))
|
|
298023
298340
|
? (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetGrid, {
|
|
298024
298341
|
key: 2,
|
|
298025
298342
|
widgets: $options.widgetsBySlot.get('header-actions'),
|
|
298026
|
-
slotName: "header-actions"
|
|
298027
|
-
|
|
298343
|
+
slotName: "header-actions",
|
|
298344
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298345
|
+
}, null, 8 /* PROPS */, ["widgets", "onSelectObject"]))
|
|
298028
298346
|
: vue$1.createCommentVNode("v-if", true),
|
|
298029
298347
|
($options.widgetsBySlot.has('footer'))
|
|
298030
298348
|
? (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetGrid, {
|
|
298031
298349
|
key: 3,
|
|
298032
298350
|
widgets: $options.widgetsBySlot.get('footer'),
|
|
298033
|
-
slotName: "footer"
|
|
298034
|
-
|
|
298351
|
+
slotName: "footer",
|
|
298352
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298353
|
+
}, null, 8 /* PROPS */, ["widgets", "onSelectObject"]))
|
|
298035
298354
|
: vue$1.createCommentVNode("v-if", true),
|
|
298036
298355
|
($options.widgetsBySlot.has('modal'))
|
|
298037
298356
|
? (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetGrid, {
|
|
298038
298357
|
key: 4,
|
|
298039
298358
|
widgets: $options.widgetsBySlot.get('modal'),
|
|
298040
|
-
slotName: "modal"
|
|
298041
|
-
|
|
298359
|
+
slotName: "modal",
|
|
298360
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298361
|
+
}, null, 8 /* PROPS */, ["widgets", "onSelectObject"]))
|
|
298042
298362
|
: vue$1.createCommentVNode("v-if", true),
|
|
298043
298363
|
($options.widgetsBySlot.has('sidebar') && $options.pageSidebarVisibleValue)
|
|
298044
298364
|
? (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetGrid, {
|
|
298045
298365
|
key: 5,
|
|
298046
298366
|
widgets: $options.widgetsBySlot.get('sidebar'),
|
|
298047
|
-
slotName: "sidebar"
|
|
298048
|
-
|
|
298367
|
+
slotName: "sidebar",
|
|
298368
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298369
|
+
}, null, 8 /* PROPS */, ["widgets", "onSelectObject"]))
|
|
298049
298370
|
: vue$1.createCommentVNode("v-if", true),
|
|
298050
298371
|
(vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.dynamicSlotKeys, (dynamicSlot) => {
|
|
298051
298372
|
return (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetGrid, {
|
|
298052
298373
|
key: dynamicSlot,
|
|
298053
298374
|
widgets: $options.widgetsBySlot.get(dynamicSlot),
|
|
298054
|
-
slotName: dynamicSlot
|
|
298055
|
-
|
|
298375
|
+
slotName: dynamicSlot,
|
|
298376
|
+
onSelectObject: $options.onRelatedObjectOpen
|
|
298377
|
+
}, null, 8 /* PROPS */, ["widgets", "slotName", "onSelectObject"]))
|
|
298056
298378
|
}), 128 /* KEYED_FRAGMENT */))
|
|
298057
298379
|
], 64 /* STABLE_FRAGMENT */))
|
|
298058
298380
|
: ($options.resolvedComponent)
|
|
@@ -298061,6 +298383,7 @@ function render$r(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
298061
298383
|
onRowClick: $options.onRowOpen,
|
|
298062
298384
|
onRowAuxClick: $options.onRowOpen,
|
|
298063
298385
|
onEditOpen: $options.onRowOpen,
|
|
298386
|
+
onRelatedObjectClick: $options.onRelatedObjectOpen,
|
|
298064
298387
|
onConfigure: _cache[1] || (_cache[1] = $event => ($data.showConfigModal = true))
|
|
298065
298388
|
}), vue$1.createSlots({ _: 2 /* DYNAMIC */ }, [
|
|
298066
298389
|
vue$1.renderList($options.resolvedSlotEntries, (entry) => {
|
|
@@ -298096,7 +298419,7 @@ function render$r(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
298096
298419
|
key: "0"
|
|
298097
298420
|
}
|
|
298098
298421
|
: undefined
|
|
298099
|
-
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["onView", "onRowClick", "onRowAuxClick", "onEditOpen"]))
|
|
298422
|
+
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["onView", "onRowClick", "onRowAuxClick", "onEditOpen", "onRelatedObjectClick"]))
|
|
298100
298423
|
: vue$1.createCommentVNode("v-if", true),
|
|
298101
298424
|
(!$options.hasRenderableBody)
|
|
298102
298425
|
? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_2$A, [
|
|
@@ -304420,6 +304743,136 @@ script$j.render = render$j;
|
|
|
304420
304743
|
script$j.__file = "src/components/CnAiCompanion/CnAiCompanion.vue";
|
|
304421
304744
|
const __sfc_main$j = script$j;
|
|
304422
304745
|
|
|
304746
|
+
/**
|
|
304747
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
|
|
304748
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
304749
|
+
*
|
|
304750
|
+
* The label a person reads for one notification rule in their settings.
|
|
304751
|
+
*
|
|
304752
|
+
* A rule is declared in a schema's `x-openregister-notifications` map under a
|
|
304753
|
+
* KEY (`caseAssigned`, `clientUpdated`, `onCreate`). The key is an identifier
|
|
304754
|
+
* for a developer. The settings pane used to print it as is, so a person saw
|
|
304755
|
+
* "substitutionRegisteredForSubstitute" next to a switch and had to guess.
|
|
304756
|
+
*
|
|
304757
|
+
* The label is taken from the first source that has one, most specific first:
|
|
304758
|
+
*
|
|
304759
|
+
* 1. the rule's own words, when OpenRegister passes them on the preference
|
|
304760
|
+
* entry (`label`, then `title`), as a plain string or a per-locale map
|
|
304761
|
+
* like the dialect's `subject` (`{ "nl": "…", "en": "…" }`);
|
|
304762
|
+
* 2. the app's own label for the rule, from the `labels` map the app hands
|
|
304763
|
+
* to CnAppRoot (`notificationLabels`), keyed `<schema>.<key>` or `<key>`;
|
|
304764
|
+
* 3. the library's wording for the generic object-event keys;
|
|
304765
|
+
* 4. the key made readable: `caseAssigned` becomes "Case assigned".
|
|
304766
|
+
*
|
|
304767
|
+
* The rule's `subject` is used only when it carries no `{{placeholder}}`: it
|
|
304768
|
+
* is the notification TITLE template, and "Case "{{title}}" assigned to you"
|
|
304769
|
+
* reads worse as a setting than the readable key does.
|
|
304770
|
+
*
|
|
304771
|
+
* Pure: no store, no fetch, no Vue.
|
|
304772
|
+
*
|
|
304773
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
304774
|
+
*/
|
|
304775
|
+
|
|
304776
|
+
/**
|
|
304777
|
+
* Pick the string for the current language out of a string or per-locale map.
|
|
304778
|
+
*
|
|
304779
|
+
* @param {string|object|null|undefined} value A string, or `{ nl, en, … }`.
|
|
304780
|
+
* @param {string} [language] The current language, e.g. `nl` or `de_DE`.
|
|
304781
|
+
* @return {string} The resolved text, or '' when there is none.
|
|
304782
|
+
*/
|
|
304783
|
+
function resolveLocalised(value, language = 'en') {
|
|
304784
|
+
if (typeof value === 'string') {
|
|
304785
|
+
return value.trim()
|
|
304786
|
+
}
|
|
304787
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
304788
|
+
return ''
|
|
304789
|
+
}
|
|
304790
|
+
const lang = String(language || 'en');
|
|
304791
|
+
const base = lang.split(/[-_]/)[0];
|
|
304792
|
+
for (const candidate of [lang, base, 'en']) {
|
|
304793
|
+
if (typeof value[candidate] === 'string' && value[candidate].trim() !== '') {
|
|
304794
|
+
return value[candidate].trim()
|
|
304795
|
+
}
|
|
304796
|
+
}
|
|
304797
|
+
const first = Object.values(value).find((text) => typeof text === 'string' && text.trim() !== '');
|
|
304798
|
+
return first ? first.trim() : ''
|
|
304799
|
+
}
|
|
304800
|
+
|
|
304801
|
+
/**
|
|
304802
|
+
* Make a rule key readable: split camelCase, snake_case and kebab-case into
|
|
304803
|
+
* words, keep acronyms, and capitalise only the first word.
|
|
304804
|
+
*
|
|
304805
|
+
* `caseAssigned` → "Case assigned", `object_created` → "Object created",
|
|
304806
|
+
* `newBRPRecord` → "New BRP record".
|
|
304807
|
+
*
|
|
304808
|
+
* @param {string} key The rule key.
|
|
304809
|
+
* @return {string} A readable sentence-case label, or the key when it has no words.
|
|
304810
|
+
*/
|
|
304811
|
+
function humaniseRuleKey(key) {
|
|
304812
|
+
const words = String(key || '')
|
|
304813
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
304814
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
304815
|
+
.split(/[\s_\-.]+/)
|
|
304816
|
+
.filter(Boolean);
|
|
304817
|
+
if (words.length === 0) {
|
|
304818
|
+
return String(key || '')
|
|
304819
|
+
}
|
|
304820
|
+
return words
|
|
304821
|
+
.map((word, index) => {
|
|
304822
|
+
const isAcronym = word.length > 1 && word === word.toUpperCase() && /[A-Z]/.test(word);
|
|
304823
|
+
if (isAcronym) {
|
|
304824
|
+
return word
|
|
304825
|
+
}
|
|
304826
|
+
const lower = word.toLowerCase();
|
|
304827
|
+
return index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower
|
|
304828
|
+
})
|
|
304829
|
+
.join(' ')
|
|
304830
|
+
}
|
|
304831
|
+
|
|
304832
|
+
/**
|
|
304833
|
+
* The label for one preference entry.
|
|
304834
|
+
*
|
|
304835
|
+
* @param {object} entry A preference entry: `{ schema, notification, label?, title?, subject? }`.
|
|
304836
|
+
* @param {object} [options] Where else a label may come from.
|
|
304837
|
+
* @param {object} [options.labels] App-supplied labels keyed `<schema>.<key>` or `<key>`;
|
|
304838
|
+
* values are strings or per-locale maps.
|
|
304839
|
+
* @param {object} [options.known] Library wording for generic keys, keyed by rule key.
|
|
304840
|
+
* @param {string} [options.language] The current language.
|
|
304841
|
+
* @return {string} The label to show.
|
|
304842
|
+
*/
|
|
304843
|
+
function notificationRuleLabel(entry, { labels = {}, known = {}, language = 'en' } = {}) {
|
|
304844
|
+
const key = String(entry?.notification ?? '');
|
|
304845
|
+
const schema = String(entry?.schema ?? '');
|
|
304846
|
+
|
|
304847
|
+
for (const field of ['label', 'title']) {
|
|
304848
|
+
const own = resolveLocalised(entry?.[field], language);
|
|
304849
|
+
if (own) {
|
|
304850
|
+
return own
|
|
304851
|
+
}
|
|
304852
|
+
}
|
|
304853
|
+
|
|
304854
|
+
const map = labels && typeof labels === 'object' ? labels : {};
|
|
304855
|
+
for (const lookup of [schema ? `${schema}.${key}` : '', key]) {
|
|
304856
|
+
if (lookup && Object.hasOwn(map, lookup)) {
|
|
304857
|
+
const fromApp = resolveLocalised(map[lookup], language);
|
|
304858
|
+
if (fromApp) {
|
|
304859
|
+
return fromApp
|
|
304860
|
+
}
|
|
304861
|
+
}
|
|
304862
|
+
}
|
|
304863
|
+
|
|
304864
|
+
if (known && typeof known[key] === 'string' && known[key]) {
|
|
304865
|
+
return known[key]
|
|
304866
|
+
}
|
|
304867
|
+
|
|
304868
|
+
const subject = resolveLocalised(entry?.subject, language);
|
|
304869
|
+
if (subject && !subject.includes('{{')) {
|
|
304870
|
+
return subject
|
|
304871
|
+
}
|
|
304872
|
+
|
|
304873
|
+
return humaniseRuleKey(key)
|
|
304874
|
+
}
|
|
304875
|
+
|
|
304423
304876
|
const PREFS_PATH = '/apps/openregister/api/notification-preferences';
|
|
304424
304877
|
|
|
304425
304878
|
var script$i = {
|
|
@@ -304443,6 +304896,28 @@ var script$i = {
|
|
|
304443
304896
|
* than an always-empty pane.
|
|
304444
304897
|
*/
|
|
304445
304898
|
cnAppId: { default: () => '' },
|
|
304899
|
+
/**
|
|
304900
|
+
* App-supplied labels for its notification rules, provided by the
|
|
304901
|
+
* CnAppRoot ancestor from its `notificationLabels` prop. Keyed
|
|
304902
|
+
* `<schema>.<key>` or `<key>`.
|
|
304903
|
+
*/
|
|
304904
|
+
cnNotificationLabels: { default: () => ({}) },
|
|
304905
|
+
},
|
|
304906
|
+
|
|
304907
|
+
props: {
|
|
304908
|
+
/**
|
|
304909
|
+
* Labels for the notification rules, keyed `<schema>.<key>` or
|
|
304910
|
+
* `<key>`; values are strings or per-locale maps (`{ nl, en }`).
|
|
304911
|
+
* Optional: inside CnAppRoot the app's `notificationLabels` arrive by
|
|
304912
|
+
* inject, and a rule without a label still reads as words, never as
|
|
304913
|
+
* its raw key. A prop given here wins over the injected map.
|
|
304914
|
+
*
|
|
304915
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
304916
|
+
*/
|
|
304917
|
+
labels: {
|
|
304918
|
+
type: Object,
|
|
304919
|
+
default: null,
|
|
304920
|
+
},
|
|
304446
304921
|
},
|
|
304447
304922
|
|
|
304448
304923
|
data() {
|
|
@@ -304568,18 +305043,43 @@ var script$i = {
|
|
|
304568
305043
|
},
|
|
304569
305044
|
|
|
304570
305045
|
/**
|
|
304571
|
-
* Human-readable label for a notification
|
|
305046
|
+
* Human-readable label for a notification rule. Never the raw key: the
|
|
305047
|
+
* rule's own label, then the app's, then the library's wording for the
|
|
305048
|
+
* generic keys, then the key split into words.
|
|
304572
305049
|
*
|
|
304573
|
-
* @param {
|
|
305050
|
+
* @param {object} entry The preference row.
|
|
304574
305051
|
* @return {string} A display label.
|
|
305052
|
+
*
|
|
305053
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
304575
305054
|
*/
|
|
304576
|
-
notificationLabel(
|
|
305055
|
+
notificationLabel(entry) {
|
|
304577
305056
|
const known = {
|
|
304578
305057
|
object_created: l10n.translate('nextcloud-vue', 'When an item is created'),
|
|
304579
305058
|
object_updated: l10n.translate('nextcloud-vue', 'When an item is updated'),
|
|
304580
305059
|
object_transitioned: l10n.translate('nextcloud-vue', 'When an item is assigned or changes status'),
|
|
304581
305060
|
};
|
|
304582
|
-
|
|
305061
|
+
const language = this.currentLanguage();
|
|
305062
|
+
const injected = typeof this.cnNotificationLabels === 'function'
|
|
305063
|
+
? this.cnNotificationLabels()
|
|
305064
|
+
: this.cnNotificationLabels;
|
|
305065
|
+
return notificationRuleLabel(entry, {
|
|
305066
|
+
labels: { ...(injected || {}), ...(this.labels || {}) },
|
|
305067
|
+
known,
|
|
305068
|
+
language,
|
|
305069
|
+
})
|
|
305070
|
+
},
|
|
305071
|
+
|
|
305072
|
+
/**
|
|
305073
|
+
* The person's language, `en` when it cannot be read.
|
|
305074
|
+
*
|
|
305075
|
+
* @return {string} The language code.
|
|
305076
|
+
*/
|
|
305077
|
+
currentLanguage() {
|
|
305078
|
+
try {
|
|
305079
|
+
return (typeof l10n.getLanguage === 'function' && l10n.getLanguage()) || 'en'
|
|
305080
|
+
} catch {
|
|
305081
|
+
return 'en'
|
|
305082
|
+
}
|
|
304583
305083
|
},
|
|
304584
305084
|
|
|
304585
305085
|
/**
|
|
@@ -304669,7 +305169,7 @@ function render$i(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
304669
305169
|
"onUpdate:modelValue": $event => ($options.onToggle(entry, $event))
|
|
304670
305170
|
}, {
|
|
304671
305171
|
default: vue$1.withCtx(() => [
|
|
304672
|
-
vue$1.createTextVNode(vue$1.toDisplayString($options.notificationLabel(entry
|
|
305172
|
+
vue$1.createTextVNode(vue$1.toDisplayString($options.notificationLabel(entry)), 1 /* TEXT */)
|
|
304673
305173
|
]),
|
|
304674
305174
|
_: 2 /* DYNAMIC */
|
|
304675
305175
|
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["modelValue", "disabled", "onUpdate:modelValue"]),
|
|
@@ -306487,6 +306987,18 @@ var script$f = {
|
|
|
306487
306987
|
* wiring.
|
|
306488
306988
|
*/
|
|
306489
306989
|
cnAppId: this.appId,
|
|
306990
|
+
/**
|
|
306991
|
+
* The app's labels for its notification rules, read by the
|
|
306992
|
+
* notification-preferences pane in the user-settings modal. A
|
|
306993
|
+
* getter so a late prop update is still seen by a pane that
|
|
306994
|
+
* mounts after it.
|
|
306995
|
+
*
|
|
306996
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
306997
|
+
*/
|
|
306998
|
+
get cnNotificationLabels() {
|
|
306999
|
+
return self.notificationLabels || {}
|
|
307000
|
+
},
|
|
307001
|
+
|
|
306490
307002
|
/**
|
|
306491
307003
|
* Target repo slug for the in-product feature-request deep
|
|
306492
307004
|
* link (e.g. `ConductionNL/pipelinq`). Read from the
|
|
@@ -306902,6 +307414,23 @@ var script$f = {
|
|
|
306902
307414
|
default: (key) => key,
|
|
306903
307415
|
},
|
|
306904
307416
|
|
|
307417
|
+
/**
|
|
307418
|
+
* Labels for the app's notification rules, shown in the
|
|
307419
|
+
* user-settings notification pane instead of the rule keys. Keyed
|
|
307420
|
+
* `<schema>.<key>` (or just `<key>`); values are already translated
|
|
307421
|
+
* strings, e.g. `{ 'case.caseAssigned': t('dossiq', 'A case is
|
|
307422
|
+
* assigned to me') }`, or per-locale maps `{ nl, en }`. A rule's own
|
|
307423
|
+
* `label` from OpenRegister wins; a rule with no label anywhere reads
|
|
307424
|
+
* as its key split into words, never as the raw key.
|
|
307425
|
+
*
|
|
307426
|
+
* @type {{[key: string]: (string|object)}}
|
|
307427
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
307428
|
+
*/
|
|
307429
|
+
notificationLabels: {
|
|
307430
|
+
type: Object,
|
|
307431
|
+
default: () => ({}),
|
|
307432
|
+
},
|
|
307433
|
+
|
|
306905
307434
|
/**
|
|
306906
307435
|
* List of permission strings the current user holds. Forwarded
|
|
306907
307436
|
* to CnAppNav's permission filter.
|
|
@@ -349472,6 +350001,7 @@ exports.getRegisterApiUrl = getRegisterApiUrl;
|
|
|
349472
350001
|
exports.getSchemaApiUrl = getSchemaApiUrl;
|
|
349473
350002
|
exports.getSharedRegistry = getSharedRegistry;
|
|
349474
350003
|
exports.getWidgetTypeEntry = getWidgetTypeEntry;
|
|
350004
|
+
exports.humaniseRuleKey = humaniseRuleKey;
|
|
349475
350005
|
exports.installIntegrationRegistry = installIntegrationRegistry;
|
|
349476
350006
|
exports.installModalStack = installModalStack;
|
|
349477
350007
|
exports.integrations = integrations;
|
|
@@ -349503,6 +350033,7 @@ exports.mergePages = mergePages;
|
|
|
349503
350033
|
exports.mergeUserLayout = mergeUserLayout;
|
|
349504
350034
|
exports.networkError = networkError;
|
|
349505
350035
|
exports.notesIntegration = notesIntegration;
|
|
350036
|
+
exports.notificationRuleLabel = notificationRuleLabel;
|
|
349506
350037
|
exports.offlineCollection = index;
|
|
349507
350038
|
exports.openRowTarget = openRowTarget;
|
|
349508
350039
|
exports.openprojectIntegration = openprojectIntegration;
|