vue-girder-slicer-cli-ui 0.0.8
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/README.md +102 -0
- package/dist/api/girderRest.d.ts +36 -0
- package/dist/api/girderSlicerApi.d.ts +32 -0
- package/dist/components/FileBrowser/DataBrowser.tw.vue.d.ts +177 -0
- package/dist/components/FileBrowser/Modal.tw.vue.d.ts +27 -0
- package/dist/components/FileBrowser/RootSelection.tw.vue.d.ts +41 -0
- package/dist/components/FileBrowser/utils.d.ts +9 -0
- package/dist/components/GirderControlsPanel.tw.vue.d.ts +29 -0
- package/dist/components/GirderSlicerParameter.tw.vue.d.ts +50 -0
- package/dist/components/GirderSlicerTaskButton.tw.vue.d.ts +42 -0
- package/dist/components/GirderSlicerTaskCard.tw.vue.d.ts +44 -0
- package/dist/components/GirderSlicerTaskMenu.tw.vue.d.ts +49 -0
- package/dist/components/GlicerSlicerTaskMenuModal.tw.vue.d.ts +34 -0
- package/dist/components/GlicerSlicerTaskMenuModalButton.tw.vue.d.ts +38 -0
- package/dist/components/GriderSlicerTasksIntegrated.tw.vue.d.ts +74 -0
- package/dist/components/Parameters/BaseParameter.tw.vue.d.ts +21 -0
- package/dist/components/Parameters/BooleanParameter.tw.vue.d.ts +18 -0
- package/dist/components/Parameters/ColorParameter.tw.vue.d.ts +19 -0
- package/dist/components/Parameters/EnumerationParameters.tw.vue.d.ts +19 -0
- package/dist/components/Parameters/FileParameter.tw.vue.d.ts +32 -0
- package/dist/components/Parameters/RangeParameter.tw.vue.d.ts +19 -0
- package/dist/favicon.ico +0 -0
- package/dist/girderTypes.d.ts +21 -0
- package/dist/index.d.ts +11 -0
- package/dist/parser/constraints.d.ts +4 -0
- package/dist/parser/convert.d.ts +7 -0
- package/dist/parser/defaultValue.d.ts +12 -0
- package/dist/parser/group.d.ts +9 -0
- package/dist/parser/index.d.ts +8 -0
- package/dist/parser/panel.d.ts +11 -0
- package/dist/parser/param.d.ts +6 -0
- package/dist/parser/parse.d.ts +20 -0
- package/dist/parser/parserTypes.d.ts +58 -0
- package/dist/parser/widget.d.ts +6 -0
- package/dist/style.css +1 -0
- package/dist/tailwind.css +1370 -0
- package/dist/utils.d.ts +5 -0
- package/dist/vue-girder-slicer-cli-ui.js +12987 -0
- package/dist/vue-girder-slicer-cli-ui.umd.cjs +59 -0
- package/package.json +135 -0
- package/src/components/FileBrowser/DataBrowser.tw.vue +842 -0
- package/src/components/FileBrowser/Modal.tw.vue +82 -0
- package/src/components/FileBrowser/RootSelection.tw.vue +83 -0
- package/src/components/FileBrowser/utils.ts +48 -0
- package/src/components/GirderControlsPanel.tw.vue +94 -0
- package/src/components/GirderSlicerParameter.tw.vue +84 -0
- package/src/components/GirderSlicerTaskButton.tw.vue +208 -0
- package/src/components/GirderSlicerTaskCard.tw.vue +184 -0
- package/src/components/GirderSlicerTaskMenu.tw.vue +217 -0
- package/src/components/GlicerSlicerTaskMenuModal.tw.vue +77 -0
- package/src/components/GlicerSlicerTaskMenuModalButton.tw.vue +106 -0
- package/src/components/GriderSlicerTasksIntegrated.tw.vue +73 -0
- package/src/components/Parameters/BaseParameter.tw.vue +76 -0
- package/src/components/Parameters/BooleanParameter.tw.vue +54 -0
- package/src/components/Parameters/ColorParameter.tw.vue +58 -0
- package/src/components/Parameters/EnumerationParameters.tw.vue +66 -0
- package/src/components/Parameters/FileParameter.tw.vue +172 -0
- package/src/components/Parameters/RangeParameter.tw.vue +74 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { PropType, Ref, computed, defineComponent, ref, watch } from 'vue';
|
|
3
|
+
import parse from '../parser/parse';
|
|
4
|
+
import GirderControlsPanel from './GirderControlsPanel.tw.vue';
|
|
5
|
+
import RestClient from '../api/girderRest';
|
|
6
|
+
import { JobResponse, useGirderSlicerApi } from '../api/girderSlicerApi';
|
|
7
|
+
import type { XMLParameters, XMLSpecification } from '../parser/parserTypes';
|
|
8
|
+
import SvgIcon from '@jamescoyle/vue-icon';
|
|
9
|
+
import { mdiClose } from '@mdi/js';
|
|
10
|
+
export default defineComponent({
|
|
11
|
+
name:'GirderSlicerTaskCard',
|
|
12
|
+
compatConfig: { mode: 2 },
|
|
13
|
+
components: {
|
|
14
|
+
GirderControlsPanel,
|
|
15
|
+
SvgIcon
|
|
16
|
+
},
|
|
17
|
+
props: {
|
|
18
|
+
apiUrl: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: 'api/v1',
|
|
21
|
+
},
|
|
22
|
+
taskId: {
|
|
23
|
+
type: String as PropType<string | null>,
|
|
24
|
+
default: '64e8aff6072d5e5fbb8719aa'
|
|
25
|
+
},
|
|
26
|
+
defaults: {
|
|
27
|
+
type: Function as PropType<(item: XMLParameters) => undefined | null | XMLParameters>,
|
|
28
|
+
default: (_item: XMLParameters) => undefined,
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
setup(props, { emit }) {
|
|
32
|
+
const girderRest = new RestClient({apiRoot: props.apiUrl});
|
|
33
|
+
girderRest
|
|
34
|
+
const loggedIn = computed(() => girderRest?.token);
|
|
35
|
+
const result: Ref<XMLSpecification | null> = ref(null);
|
|
36
|
+
const jobData: Ref<null | JobResponse> = ref(null)
|
|
37
|
+
const slicerApi = useGirderSlicerApi(girderRest);
|
|
38
|
+
const getData = async () => {
|
|
39
|
+
if (props.taskId) {
|
|
40
|
+
const response = await slicerApi.getSlicerXML(props.taskId);
|
|
41
|
+
const parseParams = parse(response.data);
|
|
42
|
+
// We need to assign default values if they exists
|
|
43
|
+
const updateParams: {panelIndex: number, groupIndex: number, parameterIndex: number, value: XMLParameters}[] = [];
|
|
44
|
+
parseParams.panels.forEach((panel, panelIndex) => {
|
|
45
|
+
panel.groups.forEach((group, groupIndex) => {
|
|
46
|
+
group.parameters.forEach((parameter, parameterIndex) => {
|
|
47
|
+
const paramResult = props.defaults(parameter);
|
|
48
|
+
if (paramResult && parseParams) {
|
|
49
|
+
// Reset the parameter
|
|
50
|
+
updateParams.push({
|
|
51
|
+
panelIndex,
|
|
52
|
+
groupIndex,
|
|
53
|
+
parameterIndex,
|
|
54
|
+
value: paramResult,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
updateParams.forEach((item) => {
|
|
61
|
+
if (parseParams) {
|
|
62
|
+
parseParams.panels[item.panelIndex].groups[item.groupIndex].parameters[item.parameterIndex] = item.value;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
result.value = parseParams;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (props.taskId) {
|
|
69
|
+
getData();
|
|
70
|
+
}
|
|
71
|
+
watch(() => props.taskId, () => {
|
|
72
|
+
getData();
|
|
73
|
+
})
|
|
74
|
+
const updateParameters = (e: XMLParameters[], index: number) => {
|
|
75
|
+
if (result.value) {
|
|
76
|
+
result.value.panels[index].groups[0].parameters = e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const runTask = async () => {
|
|
80
|
+
// First we need to validate the task has all parameters required.
|
|
81
|
+
if (result.value && props.taskId) {
|
|
82
|
+
const resp = await slicerApi.runTask(result.value, props.taskId);
|
|
83
|
+
if (resp) {
|
|
84
|
+
console.log(resp);
|
|
85
|
+
jobData.value = resp;
|
|
86
|
+
emit('run-task', jobData.value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const processInput = async (name: string) => {
|
|
91
|
+
if (result.value) {
|
|
92
|
+
await slicerApi.processInput(result.value, name);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const cancel = () => {
|
|
97
|
+
result.value = null;
|
|
98
|
+
emit('cancel')
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
result,
|
|
102
|
+
runTask,
|
|
103
|
+
cancel,
|
|
104
|
+
updateParameters,
|
|
105
|
+
processInput,
|
|
106
|
+
jobData,
|
|
107
|
+
loggedIn,
|
|
108
|
+
mdiClose,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
</script>
|
|
113
|
+
<template>
|
|
114
|
+
<div
|
|
115
|
+
v-if="result"
|
|
116
|
+
class="gsu-card relative flex flex-col min-w-0 rounded break-words border pa-2"
|
|
117
|
+
>
|
|
118
|
+
<div class="flex-auto p-6">
|
|
119
|
+
<div class="grid grid-cols-12 gap-4 pb-2">
|
|
120
|
+
<span class="col-span-12">
|
|
121
|
+
<h5>
|
|
122
|
+
{{ result.title }}
|
|
123
|
+
</h5>
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
126
|
+
<div
|
|
127
|
+
v-if="jobData"
|
|
128
|
+
class="mb-3 flex flex-wrap justify-content-left g-0"
|
|
129
|
+
>
|
|
130
|
+
<div
|
|
131
|
+
class="relative px-3 py-3 mb-4 border rounded text-green-darker border-green-dark bg-green-lighter opacity-0 opacity-100 block"
|
|
132
|
+
role="alert"
|
|
133
|
+
>
|
|
134
|
+
<span>{{ jobData.title }} running</span>
|
|
135
|
+
<svg-icon
|
|
136
|
+
type="mdi"
|
|
137
|
+
:path="mdiClose"
|
|
138
|
+
:size="30"
|
|
139
|
+
class="pb-2 gsu-icon gsu-clickable"
|
|
140
|
+
data-dismiss="modal"
|
|
141
|
+
aria-label="Close"
|
|
142
|
+
style="float:right"
|
|
143
|
+
@click="jobData = null"
|
|
144
|
+
>
|
|
145
|
+
<span aria-hidden="true">×</span>
|
|
146
|
+
</svg-icon>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
<p class="mb-0">
|
|
150
|
+
{{ result.description }}
|
|
151
|
+
</p>
|
|
152
|
+
<GirderControlsPanel
|
|
153
|
+
v-for="(panel, index) in result.panels"
|
|
154
|
+
:key="`panel_${index}`"
|
|
155
|
+
:panel="panel"
|
|
156
|
+
:collapse-override="!!jobData"
|
|
157
|
+
@change="updateParameters($event, index)"
|
|
158
|
+
@input-selected="processInput($event)"
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
<div class="flex-auto p-6">
|
|
162
|
+
<div class="grid gap-4 pb-2 justify-items-end">
|
|
163
|
+
<span>
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
class="bg-red-500 border-solid border-borderColor font-bold py-2 px-4 mx-2 rounded border-2"
|
|
167
|
+
@click="cancel()"
|
|
168
|
+
>
|
|
169
|
+
Cancel
|
|
170
|
+
</button>
|
|
171
|
+
<button
|
|
172
|
+
type="button"
|
|
173
|
+
class="gsu-btn-accept font-bold py-2 px-4 mx-2 rounded border-2"
|
|
174
|
+
@click="runTask()"
|
|
175
|
+
>
|
|
176
|
+
Run
|
|
177
|
+
</button>
|
|
178
|
+
</span>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</template>
|
|
183
|
+
<style scoped>
|
|
184
|
+
</style>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Ref, computed, defineComponent, onMounted, ref } from "vue";
|
|
3
|
+
import { extractImageInfo } from "../utils";
|
|
4
|
+
import RestClient from "../api/girderRest";
|
|
5
|
+
import { SlicerImage, useGirderSlicerApi } from "../api/girderSlicerApi";
|
|
6
|
+
import { mdiAlert, mdiChevronDown, mdiChevronRight } from "@mdi/js";
|
|
7
|
+
import SvgIcon from "@jamescoyle/vue-icon";
|
|
8
|
+
import { cloneDeep } from 'lodash';
|
|
9
|
+
import { PropType } from "vue/types/v3-component-props";
|
|
10
|
+
|
|
11
|
+
interface TaskInfo {
|
|
12
|
+
tag: string;
|
|
13
|
+
tasks: {
|
|
14
|
+
imageBase: string;
|
|
15
|
+
imageTag: string;
|
|
16
|
+
_id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type TaskHierarchy = Record<
|
|
23
|
+
string,
|
|
24
|
+
[TaskInfo]
|
|
25
|
+
>;
|
|
26
|
+
export default defineComponent({
|
|
27
|
+
components: {
|
|
28
|
+
SvgIcon,
|
|
29
|
+
},
|
|
30
|
+
props: {
|
|
31
|
+
apiUrl: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: "api/v1",
|
|
34
|
+
},
|
|
35
|
+
filter: {
|
|
36
|
+
type: Function as PropType<(task: SlicerImage) => boolean>,
|
|
37
|
+
default: (_task: SlicerImage) => true,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
setup(props, { emit }) {
|
|
41
|
+
const girderRest = new RestClient({ apiRoot: props.apiUrl });
|
|
42
|
+
const loggedIn = computed(() => girderRest?.token);
|
|
43
|
+
const slicerApi = useGirderSlicerApi(girderRest);
|
|
44
|
+
const results: Ref<TaskHierarchy> = ref({});
|
|
45
|
+
const clicked: Ref<Record<string, boolean>> = ref({})
|
|
46
|
+
const selected: Ref<null | string> = ref(null);
|
|
47
|
+
const getData = async () => {
|
|
48
|
+
try {
|
|
49
|
+
const response = await slicerApi.getSlicerList();
|
|
50
|
+
const filtered = response.data.filter((task) => props.filter(task));
|
|
51
|
+
// ground items by their image
|
|
52
|
+
const taskHierarchy: TaskHierarchy = {};
|
|
53
|
+
filtered.forEach((task) => {
|
|
54
|
+
// Lets get the base image name
|
|
55
|
+
const imageInfo = extractImageInfo(task.image);
|
|
56
|
+
if (imageInfo) {
|
|
57
|
+
// We create a root for it
|
|
58
|
+
if (taskHierarchy[imageInfo.baseName] === undefined) {
|
|
59
|
+
clicked.value[imageInfo.baseName] = false;
|
|
60
|
+
taskHierarchy[imageInfo.baseName] = [
|
|
61
|
+
{ tag: imageInfo.tag, tasks: [] },
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
const foundIndex = taskHierarchy[imageInfo.baseName].findIndex(
|
|
65
|
+
(item) => item.tag === imageInfo.tag
|
|
66
|
+
);
|
|
67
|
+
if (foundIndex !== -1) {
|
|
68
|
+
clicked.value[`${imageInfo.baseName}.${imageInfo.tag}`] = false;
|
|
69
|
+
taskHierarchy[imageInfo.baseName][foundIndex].tasks.push({
|
|
70
|
+
imageBase: imageInfo.baseName,
|
|
71
|
+
imageTag: imageInfo.tag,
|
|
72
|
+
_id: task._id,
|
|
73
|
+
name: task.name,
|
|
74
|
+
description: task.description,
|
|
75
|
+
});
|
|
76
|
+
} else if (foundIndex === -1) {
|
|
77
|
+
taskHierarchy[imageInfo.baseName].push({
|
|
78
|
+
tag: imageInfo.tag,
|
|
79
|
+
tasks: [
|
|
80
|
+
{
|
|
81
|
+
imageBase: imageInfo.baseName,
|
|
82
|
+
imageTag: imageInfo.tag,
|
|
83
|
+
_id: task._id,
|
|
84
|
+
name: task.name,
|
|
85
|
+
description: task.description,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
results.value = taskHierarchy;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error("Cannot communicate with server");
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const select = (id: string, name: string) => {
|
|
98
|
+
emit("selected", {id, name});
|
|
99
|
+
selected.value = id;
|
|
100
|
+
};
|
|
101
|
+
onMounted(() => {
|
|
102
|
+
getData();
|
|
103
|
+
});
|
|
104
|
+
const selectItem = (item: string) => {
|
|
105
|
+
const tempClicked = cloneDeep(clicked.value);
|
|
106
|
+
if (tempClicked[item]) {
|
|
107
|
+
//Then we toggle it off and all children
|
|
108
|
+
tempClicked[item] = false;
|
|
109
|
+
} else {
|
|
110
|
+
Object.keys(tempClicked).forEach((key) => tempClicked[key] = false );
|
|
111
|
+
const splits = item.split('.');
|
|
112
|
+
const builtString: string[] = [];
|
|
113
|
+
splits.forEach((key) => {
|
|
114
|
+
builtString.push(key);
|
|
115
|
+
const testString = builtString.join('.');
|
|
116
|
+
if (tempClicked[testString] !== undefined) {
|
|
117
|
+
tempClicked[testString] = true;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
clicked.value = tempClicked;
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
results,
|
|
126
|
+
mdiAlert,
|
|
127
|
+
mdiChevronDown,
|
|
128
|
+
mdiChevronRight,
|
|
129
|
+
loggedIn,
|
|
130
|
+
select,
|
|
131
|
+
clicked,
|
|
132
|
+
selectItem,
|
|
133
|
+
selected,
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
</script>
|
|
138
|
+
<template>
|
|
139
|
+
<div>
|
|
140
|
+
<div v-if="loggedIn">
|
|
141
|
+
<ul
|
|
142
|
+
class="text-textColor"
|
|
143
|
+
>
|
|
144
|
+
<li
|
|
145
|
+
v-for="(item, key) in results"
|
|
146
|
+
:key="key"
|
|
147
|
+
>
|
|
148
|
+
<span class="gsu-menu-item rounded-t py-2 px-4 block whitespace-no-wrap" @click="selectItem(item.length === 1 ? `${key}.${item[0].tag}` : key )" >
|
|
149
|
+
<span class="pr-1 flex-1"
|
|
150
|
+
>{{ item.length === 1 ? `${key}:${item[0].tag}` : key }}
|
|
151
|
+
</span>
|
|
152
|
+
<span class="mr-auto">
|
|
153
|
+
<svg-icon type="mdi" :path="clicked[item.length === 1 ? `${key}.${item[0].tag}` : key] ? mdiChevronDown : mdiChevronRight" :size="30" class="pb-1" style="display:inline" />
|
|
154
|
+
</span>
|
|
155
|
+
</span>
|
|
156
|
+
<ul
|
|
157
|
+
v-if="item.length === 1 && clicked[`${key}.${item[0].tag}`]"
|
|
158
|
+
class="text-textColor"
|
|
159
|
+
>
|
|
160
|
+
<li
|
|
161
|
+
v-for="task in item[0].tasks"
|
|
162
|
+
:key="task._id"
|
|
163
|
+
class="gsu-menu-item py-2 px-4 block whitespace-no-wrap pl-8"
|
|
164
|
+
@click="select(task._id, `${key}:${item[0].tag} -> ${task.name}`)"
|
|
165
|
+
:class="`${task._id === selected ? 'bg-highlightedColor': ''}`"
|
|
166
|
+
>
|
|
167
|
+
{{ task.name }}
|
|
168
|
+
</li>
|
|
169
|
+
</ul>
|
|
170
|
+
<ul
|
|
171
|
+
v-else-if="clicked[key] && item.length > 1"
|
|
172
|
+
class="text-textColor"
|
|
173
|
+
>
|
|
174
|
+
<li
|
|
175
|
+
v-for="tag in item"
|
|
176
|
+
:key="tag.tag"
|
|
177
|
+
>
|
|
178
|
+
<span class="py-2 px-4 block whitespace-no-wrap pl-8" @click="selectItem(`${key}.${tag.tag}`)">
|
|
179
|
+
<span class="pr-1">{{ tag.tag }}</span>
|
|
180
|
+
<span class="mr-auto">
|
|
181
|
+
<svg-icon type="mdi" :path="clicked[`${key}.${tag.tag}`] ? mdiChevronDown : mdiChevronRight" :size="30" class="pb-1" style="display:inline" />
|
|
182
|
+
|
|
183
|
+
</span>
|
|
184
|
+
</span>
|
|
185
|
+
<ul
|
|
186
|
+
v-if="clicked[`${key}.${tag.tag}`]"
|
|
187
|
+
class="text-textColor"
|
|
188
|
+
>
|
|
189
|
+
<li
|
|
190
|
+
v-for="task in tag.tasks"
|
|
191
|
+
:key="task._id"
|
|
192
|
+
@click="select(task._id,`${key}:${tag.tag} -> ${task.name}` )"
|
|
193
|
+
class="py-2 px-4 block whitespace-no-wrap pl-12"
|
|
194
|
+
:class="`${task._id === selected ? 'bg-bgMutedColor': ''}`"
|
|
195
|
+
>
|
|
196
|
+
<span class=" py-2 px-4 block whitespace-no-wrap" href="#">
|
|
197
|
+
{{ task.name }}
|
|
198
|
+
</span>
|
|
199
|
+
</li>
|
|
200
|
+
</ul>
|
|
201
|
+
</li>
|
|
202
|
+
</ul>
|
|
203
|
+
</li>
|
|
204
|
+
</ul>
|
|
205
|
+
</div>
|
|
206
|
+
<div v-else class="mx-6">
|
|
207
|
+
<span>
|
|
208
|
+
Error
|
|
209
|
+
<svg-icon style="display:inline" type="mdi" color="orange" :path="mdiAlert" :size="30" class="pb-1" />
|
|
210
|
+
</span>
|
|
211
|
+
<div>The User is not logged in.</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</template>
|
|
215
|
+
<style scoped lang="scss">
|
|
216
|
+
|
|
217
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { defineComponent, ref } from "vue";
|
|
3
|
+
import SvgIcon from "@jamescoyle/vue-icon";
|
|
4
|
+
import Modal from './FileBrowser/Modal.tw.vue';
|
|
5
|
+
import GirderSlicerTaskMenu from "./GirderSlicerTaskMenu.tw.vue";
|
|
6
|
+
import { SlicerImage } from "../api/girderSlicerApi";
|
|
7
|
+
import { PropType } from "vue/types/v3-component-props";
|
|
8
|
+
|
|
9
|
+
export default defineComponent({
|
|
10
|
+
components: {
|
|
11
|
+
SvgIcon,
|
|
12
|
+
Modal,
|
|
13
|
+
GirderSlicerTaskMenu,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
apiUrl: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: "api/v1",
|
|
19
|
+
},
|
|
20
|
+
filter: {
|
|
21
|
+
type: Function as PropType<(task: SlicerImage) => boolean>,
|
|
22
|
+
default: (_task: SlicerImage) => true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
setup(_props, { emit }) {
|
|
26
|
+
const accept = () => {
|
|
27
|
+
emit('selected', selectedId.value);
|
|
28
|
+
};
|
|
29
|
+
const selectedId = ref('');
|
|
30
|
+
const selectedName = ref('');
|
|
31
|
+
const processSelected = (data: { id: string, name: string }) => {
|
|
32
|
+
selectedId.value = data.id;
|
|
33
|
+
selectedName.value = data.name;
|
|
34
|
+
}
|
|
35
|
+
const selected = ref('');
|
|
36
|
+
return {
|
|
37
|
+
accept,
|
|
38
|
+
processSelected,
|
|
39
|
+
selected,
|
|
40
|
+
selectedId,
|
|
41
|
+
selectedName,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
<template>
|
|
47
|
+
<span>
|
|
48
|
+
<Modal
|
|
49
|
+
:disabled-confirm="!selectedName"
|
|
50
|
+
@cancel="$emit('cancel')"
|
|
51
|
+
@confirm="accept()"
|
|
52
|
+
>
|
|
53
|
+
<template slot="header">
|
|
54
|
+
<span>Task Selection</span>
|
|
55
|
+
</template>
|
|
56
|
+
<template slot="body">
|
|
57
|
+
<GirderSlicerTaskMenu
|
|
58
|
+
:api-url="apiUrl"
|
|
59
|
+
:filter="filter"
|
|
60
|
+
@selected="processSelected($event)"
|
|
61
|
+
/>
|
|
62
|
+
<div v-if="selectedName" class="flex flex-row justify-items-center">
|
|
63
|
+
<div class="ma-auto">
|
|
64
|
+
<span class="px-2">Selected:</span>
|
|
65
|
+
<span class="px-2" > {{ selectedName }}</span>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
</Modal>
|
|
70
|
+
</span>
|
|
71
|
+
</template>
|
|
72
|
+
<style scoped lang="scss">
|
|
73
|
+
.dropdown:hover > .dropdown-content {
|
|
74
|
+
display: block;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { computed, defineComponent, ref } from "vue";
|
|
3
|
+
import SvgIcon from "@jamescoyle/vue-icon";
|
|
4
|
+
import Modal from './FileBrowser/Modal.tw.vue';
|
|
5
|
+
import GirderSlicerTaskMenu from "./GirderSlicerTaskMenu.tw.vue";
|
|
6
|
+
import RestClient from "../api/girderRest";
|
|
7
|
+
import { mdiAlert } from "@mdi/js";
|
|
8
|
+
import { PropType } from "vue/types/v3-component-props";
|
|
9
|
+
import { SlicerImage } from "../api/girderSlicerApi";
|
|
10
|
+
export default defineComponent({
|
|
11
|
+
components: {
|
|
12
|
+
SvgIcon,
|
|
13
|
+
Modal,
|
|
14
|
+
GirderSlicerTaskMenu,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
apiUrl: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: "api/v1",
|
|
20
|
+
},
|
|
21
|
+
filter: {
|
|
22
|
+
type: Function as PropType<(task: SlicerImage) => boolean>,
|
|
23
|
+
default: (_task: SlicerImage) => true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
setup(props, { emit }) {
|
|
27
|
+
const modalOpen = ref(false);
|
|
28
|
+
const girderRest = new RestClient({ apiRoot: props.apiUrl });
|
|
29
|
+
const loggedIn = computed(() => girderRest?.token);
|
|
30
|
+
|
|
31
|
+
const accept = () => {
|
|
32
|
+
emit('selected', selectedId.value);
|
|
33
|
+
modalOpen.value = false;
|
|
34
|
+
};
|
|
35
|
+
const selectedId = ref('');
|
|
36
|
+
const selectedName = ref('');
|
|
37
|
+
const processSelected = (data: { id: string, name: string }) => {
|
|
38
|
+
selectedId.value = data.id;
|
|
39
|
+
selectedName.value = data.name;
|
|
40
|
+
}
|
|
41
|
+
const selected = ref('');
|
|
42
|
+
|
|
43
|
+
const clearSelection = () => {
|
|
44
|
+
selectedId.value = '';
|
|
45
|
+
selectedName.value = '';
|
|
46
|
+
selected.value = '';
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
modalOpen,
|
|
50
|
+
accept,
|
|
51
|
+
processSelected,
|
|
52
|
+
clearSelection,
|
|
53
|
+
selected,
|
|
54
|
+
selectedId,
|
|
55
|
+
selectedName,
|
|
56
|
+
loggedIn,
|
|
57
|
+
mdiAlert,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
<template>
|
|
63
|
+
<span>
|
|
64
|
+
<button
|
|
65
|
+
class="gsu-btn font-semibold py-2 px-4 rounded inline-flex items-center
|
|
66
|
+
rounded-sm flex items-center min-w-32 border-2 border-solid border-borderColor"
|
|
67
|
+
@click="modalOpen = true"
|
|
68
|
+
>
|
|
69
|
+
<span v-if="!loggedIn" class="pr-1 font-semibold flex-1">
|
|
70
|
+
Log In
|
|
71
|
+
<svg-icon style="display:inline" type="mdi" color="orange" :path="mdiAlert" :size="30" class="pb-1" />
|
|
72
|
+
</span>
|
|
73
|
+
<span v-else-if="!selectedName" class="pr-1 font-semibold flex-1">Choose Task</span>
|
|
74
|
+
<span v-else-if="selectedName" class="pr-1 text-xs font-semibold flex-1">{{ selectedName }}</span>
|
|
75
|
+
</button>
|
|
76
|
+
<Modal
|
|
77
|
+
:isVisible="modalOpen"
|
|
78
|
+
:disabled-confirm="!selectedName"
|
|
79
|
+
@cancel="modalOpen =false; $emit('cancel')"
|
|
80
|
+
@confirm="accept()"
|
|
81
|
+
>
|
|
82
|
+
<template slot="header">
|
|
83
|
+
<span>Task Selection</span>
|
|
84
|
+
</template>
|
|
85
|
+
<template slot="body">
|
|
86
|
+
<GirderSlicerTaskMenu
|
|
87
|
+
:api-url="apiUrl"
|
|
88
|
+
:filter="filter"
|
|
89
|
+
@selected="processSelected($event)"
|
|
90
|
+
/>
|
|
91
|
+
<div v-if="selectedName" class="flex flex-row justify-items-center">
|
|
92
|
+
<div class="ma-auto">
|
|
93
|
+
<span class="px-2">Selected:</span>
|
|
94
|
+
<span class="px-2" > {{ selectedName }}</span>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
</Modal>
|
|
99
|
+
</span>
|
|
100
|
+
</template>
|
|
101
|
+
<style scoped lang="scss">
|
|
102
|
+
.dropdown:hover > .dropdown-content {
|
|
103
|
+
display: block;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Ref, defineComponent, ref, PropType } from 'vue';
|
|
3
|
+
import GirderSlicerTaskMenuModalButton from './GlicerSlicerTaskMenuModalButton.tw.vue';
|
|
4
|
+
import GirderSlicerTaskCard from './GirderSlicerTaskCard.tw.vue';
|
|
5
|
+
import type { XMLParameters } from '../parser/parserTypes';
|
|
6
|
+
import { SlicerImage } from '../api/girderSlicerApi';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export default defineComponent({
|
|
10
|
+
name: 'GirderSlicerTasksIntegrated',
|
|
11
|
+
components: {
|
|
12
|
+
GirderSlicerTaskCard,
|
|
13
|
+
GirderSlicerTaskMenuModalButton,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
apiUrl: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: 'api/v1',
|
|
19
|
+
},
|
|
20
|
+
filter: {
|
|
21
|
+
type: Function as PropType<(task: SlicerImage) => boolean>,
|
|
22
|
+
default: (_task: SlicerImage) => true,
|
|
23
|
+
},
|
|
24
|
+
defaults: {
|
|
25
|
+
type: Function as PropType<(item: XMLParameters) => undefined | null | XMLParameters>,
|
|
26
|
+
default: (_item: XMLParameters) => undefined,
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
setup() {
|
|
30
|
+
const selected: Ref<string | null> = ref(null);
|
|
31
|
+
const menuModelButton: Ref<typeof GirderSlicerTaskMenuModalButton | null> = ref(null);
|
|
32
|
+
const select = (id: string) => {
|
|
33
|
+
selected.value = id;
|
|
34
|
+
}
|
|
35
|
+
const cancel = () => {
|
|
36
|
+
if (menuModelButton.value) {
|
|
37
|
+
menuModelButton.value.clearSelection();
|
|
38
|
+
}
|
|
39
|
+
selected.value = null;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
selected,
|
|
43
|
+
select,
|
|
44
|
+
cancel,
|
|
45
|
+
menuModelButton,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<div
|
|
54
|
+
class="card"
|
|
55
|
+
>
|
|
56
|
+
<div class="card-body">
|
|
57
|
+
<div class="card-title justify-content-center row g-20">
|
|
58
|
+
<div class="col">
|
|
59
|
+
<girder-slicer-task-menu-modal-button
|
|
60
|
+
ref="menuModelButton"
|
|
61
|
+
:filter="filter"
|
|
62
|
+
@selected="select($event)"
|
|
63
|
+
/>
|
|
64
|
+
<girder-slicer-task-card :task-id="selected" :defaults="defaults" @cancel="cancel()" @run-task="$emit('run-task', $event)" />
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
|
|
73
|
+
</style>
|