@geode/opengeodeweb-front 10.2.0 → 10.2.1-rc.2

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.
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <v-hover v-slot="{ isHovering, props: hoverProps }">
3
+ <v-card
4
+ v-bind="hoverProps"
5
+ class="text-center cursor-pointer"
6
+ :class="{
7
+ 'elevation-4': isHovering || isDragging,
8
+ 'elevation-0': !(isHovering || isDragging),
9
+ }"
10
+ :style="{
11
+ position: 'relative',
12
+ overflow: 'hidden',
13
+ transition: 'all 0.3s ease',
14
+ background:
15
+ isHovering || isDragging
16
+ ? 'rgba(var(--v-theme-primary), 0.05)'
17
+ : 'rgba(0, 0, 0, 0.02)',
18
+ border: `2px dashed ${
19
+ isHovering || isDragging ? 'rgb(var(--v-theme-primary))' : '#e0e0e0'
20
+ }`,
21
+ transform: isHovering || isDragging ? 'translateY(-2px)' : 'none',
22
+ pointerEvents: loading ? 'none' : 'auto',
23
+ opacity: loading ? 0.6 : 1,
24
+ }"
25
+ rounded="xl"
26
+ @click="triggerFileDialog"
27
+ @dragover.prevent="isDragging = true"
28
+ @dragleave.prevent="isDragging = false"
29
+ @drop.prevent="handleDrop"
30
+ >
31
+ <v-card-text class="pa-8">
32
+ <v-sheet
33
+ class="mx-auto mb-6 d-flex align-center justify-center"
34
+ :color="isHovering || isDragging ? 'primary' : 'grey-lighten-2'"
35
+ :elevation="isHovering || isDragging ? 4 : 0"
36
+ rounded="circle"
37
+ width="80"
38
+ height="80"
39
+ style="transition: all 0.3s ease"
40
+ >
41
+ <v-icon
42
+ :icon="loading ? 'mdi-loading' : 'mdi-cloud-upload'"
43
+ size="40"
44
+ :color="isHovering || isDragging ? 'white' : 'grey-darken-1'"
45
+ :class="{ rotating: loading }"
46
+ />
47
+ </v-sheet>
48
+
49
+ <v-card-title
50
+ class="text-h6 font-weight-bold justify-center pa-0 mb-1"
51
+ :class="
52
+ isHovering || isDragging ? 'text-primary' : 'text-grey-darken-2'
53
+ "
54
+ style="transition: color 0.3s ease"
55
+ >
56
+ {{ loading ? loadingText : isDragging ? dropText : idleText }}
57
+ </v-card-title>
58
+
59
+ <v-card-subtitle v-if="showExtensions" class="text-body-2 pa-0">
60
+ {{ accept ? `(${accept} files)` : "All files allowed" }}
61
+ </v-card-subtitle>
62
+ </v-card-text>
63
+
64
+ <input
65
+ ref="fileInput"
66
+ type="file"
67
+ class="d-none"
68
+ :multiple="multiple"
69
+ :accept="accept"
70
+ @change="handleFileSelect"
71
+ />
72
+ </v-card>
73
+ </v-hover>
74
+ </template>
75
+
76
+ <script setup>
77
+ const props = defineProps({
78
+ multiple: { type: Boolean, default: false },
79
+ accept: { type: String, default: "" },
80
+ loading: { type: Boolean, default: false },
81
+ showExtensions: { type: Boolean, default: true },
82
+ idleText: { type: String, default: "Click or Drag & Drop files" },
83
+ dropText: { type: String, default: "Drop to upload" },
84
+ loadingText: { type: String, default: "Uploading..." },
85
+ })
86
+
87
+ const emit = defineEmits(["files-selected"])
88
+
89
+ const isDragging = ref(false)
90
+ const fileInput = ref(null)
91
+
92
+ const triggerFileDialog = () => fileInput.value?.click()
93
+
94
+ function handleDrop(e) {
95
+ isDragging.value = false
96
+ const files = Array.from(e.dataTransfer.files)
97
+ emit("files-selected", files)
98
+ }
99
+
100
+ function handleFileSelect(e) {
101
+ const files = Array.from(e.target.files)
102
+ emit("files-selected", files)
103
+ e.target.value = ""
104
+ }
105
+ </script>
106
+
107
+ <style scoped>
108
+ .rotating {
109
+ animation: rotate 1s linear infinite;
110
+ }
111
+
112
+ @keyframes rotate {
113
+ from {
114
+ transform: rotate(0deg);
115
+ }
116
+ to {
117
+ transform: rotate(360deg);
118
+ }
119
+ }
120
+ </style>
@@ -15,6 +15,7 @@
15
15
  import { useGeodeStore } from "@ogw_front/stores/geode"
16
16
 
17
17
  const schema = schemas.opengeodeweb_back.allowed_files
18
+
18
19
  const emit = defineEmits([
19
20
  "update_values",
20
21
  "increment_step",
@@ -23,12 +24,10 @@
23
24
 
24
25
  const props = defineProps({
25
26
  multiple: { type: Boolean, required: true },
26
- files: { type: Array, required: false, default: [] },
27
- auto_upload: { type: Boolean, required: false, default: true },
27
+ files: { type: Array, default: () => [] },
28
+ auto_upload: { type: Boolean, default: true },
28
29
  })
29
30
 
30
- const { multiple } = props
31
-
32
31
  const internal_files = ref(props.files)
33
32
  const auto_upload = ref(props.auto_upload)
34
33
  const accept = ref("")
@@ -36,14 +35,13 @@
36
35
 
37
36
  watch(
38
37
  () => props.files,
39
- (newVal) => {
40
- internal_files.value = newVal
41
- },
42
- { deep: true },
38
+ (val) => (internal_files.value = val),
39
+ )
40
+
41
+ watch(
42
+ () => props.auto_upload,
43
+ (val) => (auto_upload.value = val),
43
44
  )
44
- watch(props.auto_upload, (newVal) => {
45
- auto_upload.value = newVal
46
- })
47
45
 
48
46
  const toggle_loading = useToggle(loading)
49
47
 
@@ -58,10 +56,9 @@
58
56
  toggle_loading()
59
57
  const geodeStore = useGeodeStore()
60
58
  const response = await geodeStore.request(schema, {})
61
- accept.value = response.extensions
62
- .map((extension) => "." + extension)
63
- .join(",")
59
+ accept.value = response.extensions.map((e) => `.${e}`).join(",")
64
60
  toggle_loading()
65
61
  }
62
+
66
63
  await get_allowed_files()
67
64
  </script>
@@ -1,43 +1,65 @@
1
1
  <template>
2
- <v-row>
3
- <v-col class="pa-0">
4
- <v-file-input
5
- v-model="internal_files"
6
- :multiple="props.multiple"
7
- :label="label"
8
- :accept="props.accept"
9
- :rules="[(value) => !!value || 'The file is mandatory']"
10
- color="primary"
11
- :hide-input="props.mini"
12
- :hide-details="props.mini"
13
- chips
14
- counter
15
- show-size
16
- @click:clear="clear()"
17
- />
18
- </v-col>
19
- </v-row>
20
- <v-row v-if="!props.auto_upload">
21
- <v-col cols="auto">
22
- <v-btn
2
+ <DragAndDrop
3
+ :multiple="props.multiple"
4
+ :accept="props.accept"
5
+ :loading="loading"
6
+ :show-extensions="false"
7
+ @files-selected="processSelectedFiles"
8
+ />
9
+
10
+ <v-card-text v-if="internal_files.length" class="mt-4">
11
+ <v-sheet class="d-flex align-center mb-3" color="transparent">
12
+ <v-icon icon="mdi-file-check" class="mr-2" color="primary" />
13
+ <span class="text-subtitle-2 font-weight-bold"> Selected Files </span>
14
+ <v-chip size="x-small" class="ml-2" color="primary" variant="flat">
15
+ {{ internal_files.length }}
16
+ </v-chip>
17
+ </v-sheet>
18
+
19
+ <v-sheet class="d-flex flex-wrap gap-2" color="transparent">
20
+ <v-chip
21
+ v-for="(file, index) in internal_files"
22
+ :key="index"
23
+ closable
24
+ size="small"
23
25
  color="primary"
24
- :disabled="!internal_files.length && !files_uploaded"
25
- :loading="loading"
26
- class="pa-2"
27
- @click="upload_files"
26
+ variant="tonal"
27
+ class="font-weight-medium"
28
+ @click:close="removeFile(index)"
29
+ >
30
+ <v-icon start size="16">mdi-file-outline</v-icon>
31
+ {{ file.name }}
32
+ </v-chip>
33
+ </v-sheet>
34
+ </v-card-text>
35
+
36
+ <v-card-actions v-if="!props.auto_upload && internal_files.length">
37
+ <v-btn
38
+ color="primary"
39
+ variant="elevated"
40
+ size="large"
41
+ rounded="lg"
42
+ :loading="loading"
43
+ class="text-none px-3 font-weight-bold"
44
+ @click="upload_files"
45
+ >
46
+ <v-icon start size="20">mdi-cloud-upload-outline</v-icon>
47
+ Upload {{ internal_files.length }} file<span
48
+ v-if="internal_files.length > 1"
49
+ >s</span
28
50
  >
29
- Upload file(s)
30
- </v-btn>
31
- </v-col>
32
- </v-row>
51
+ </v-btn>
52
+ </v-card-actions>
33
53
  </template>
34
54
 
35
55
  <script setup>
36
56
  import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
37
57
  import { upload_file } from "@ogw_front/utils/upload_file"
58
+ import DragAndDrop from "@ogw_front/components/DragAndDrop"
59
+
38
60
  const schema = schemas.opengeodeweb_back.upload_file
39
61
 
40
- const emit = defineEmits(["files_uploaded", "decrement_step"])
62
+ const emit = defineEmits(["files_uploaded", "decrement_step", "reset_values"])
41
63
 
42
64
  const props = defineProps({
43
65
  multiple: { type: Boolean, required: true },
@@ -47,15 +69,28 @@
47
69
  mini: { type: Boolean, required: false, default: false },
48
70
  })
49
71
 
50
- const label = props.multiple
51
- ? "Please select file(s) to import"
52
- : "Please select a file to import"
53
72
  const internal_files = ref(props.files)
54
73
  const loading = ref(false)
55
74
  const files_uploaded = ref(false)
56
75
 
57
76
  const toggle_loading = useToggle(loading)
58
77
 
78
+ function processSelectedFiles(files) {
79
+ if (props.multiple) {
80
+ internal_files.value = [...internal_files.value, ...files]
81
+ } else {
82
+ internal_files.value = [files[0]]
83
+ }
84
+ }
85
+
86
+ function removeFile(index) {
87
+ internal_files.value.splice(index, 1)
88
+ if (internal_files.value.length === 0) {
89
+ files_uploaded.value = false
90
+ emit("files_uploaded", [])
91
+ }
92
+ }
93
+
59
94
  async function upload_files() {
60
95
  toggle_loading()
61
96
  var promise_array = []
@@ -92,11 +127,6 @@
92
127
  }
93
128
  }
94
129
 
95
- function clear() {
96
- internal_files.value = []
97
- emit("files_uploaded", internal_files.value)
98
- }
99
-
100
130
  watch(
101
131
  () => props.files,
102
132
  (newVal) => {
@@ -107,17 +137,8 @@
107
137
 
108
138
  watch(internal_files, (value) => {
109
139
  files_uploaded.value = false
110
- if (props.auto_upload) {
111
- if (props.multiple.value == false) {
112
- internal_files.value = [value]
113
- }
140
+ if (props.auto_upload && value.length > 0) {
114
141
  upload_files()
115
142
  }
116
143
  })
117
144
  </script>
118
-
119
- <style scoped>
120
- .div.v-input__details {
121
- display: none;
122
- }
123
- </style>
@@ -8,7 +8,7 @@
8
8
  align-self="center"
9
9
  style="z-index: 1000"
10
10
  >
11
- <Recaptcha :color="'secondary'" />
11
+ <Recaptcha :button_color="'secondary'" />
12
12
  </v-col>
13
13
  <v-col v-else-if="infraStore.status == Status.CREATING">
14
14
  <Loading />
@@ -0,0 +1,55 @@
1
+ <script setup>
2
+ import { useTheme } from "vuetify"
3
+
4
+ const theme = useTheme()
5
+ const primaryColor = computed(() => theme.current.value.colors.primary)
6
+
7
+ defineProps({
8
+ title: { type: String, default: "" },
9
+ width: { type: [Number, String], default: 320 },
10
+ maxHeight: { type: [Number, String], default: 500 },
11
+ })
12
+ </script>
13
+ <template>
14
+ <v-card
15
+ @click.stop
16
+ :title="title"
17
+ class="option-card rounded-xl border-thin elevation-24"
18
+ :width="width"
19
+ :max-height="maxHeight"
20
+ :ripple="false"
21
+ theme="dark"
22
+ >
23
+ <v-card-text class="pa-5">
24
+ <slot />
25
+ </v-card-text>
26
+ <v-card-actions v-if="$slots.actions" class="justify-center pb-4">
27
+ <slot name="actions" />
28
+ </v-card-actions>
29
+ </v-card>
30
+ </template>
31
+
32
+ <style scoped>
33
+ .option-card {
34
+ background-color: rgba(30, 30, 30, 0.85) !important;
35
+ backdrop-filter: blur(20px);
36
+ -webkit-backdrop-filter: blur(20px);
37
+ border-color: rgba(255, 255, 255, 0.15) !important;
38
+ }
39
+
40
+ ::v-deep(.v-list-item:hover) {
41
+ background-color: rgba(255, 255, 255, 0.1);
42
+ }
43
+
44
+ ::v-deep(.v-slider-track__fill),
45
+ ::v-deep(.v-selection-control--dirty .v-switch__track) {
46
+ background-color: v-bind(primaryColor) !important;
47
+ }
48
+
49
+ ::v-deep(.v-btn) {
50
+ border-radius: 8px;
51
+ text-transform: none;
52
+ font-weight: 500;
53
+ letter-spacing: normal;
54
+ }
55
+ </style>
@@ -31,7 +31,7 @@
31
31
  <VCol cols="4" class="d-flex justify-center align-center">
32
32
  <VBtn
33
33
  :text="props.button_label"
34
- :color="props.button_color"
34
+ :color="props.color || props.button_color"
35
35
  @click="submit_recaptcha"
36
36
  />
37
37
  </VCol>
@@ -53,11 +53,16 @@
53
53
  required: false,
54
54
  default: "white",
55
55
  },
56
+ color: {
57
+ type: String,
58
+ required: false,
59
+ },
56
60
  })
57
61
  const infraStore = useInfraStore()
58
62
  const name = ref("")
59
63
  const email = ref("")
60
64
  const launch = ref(false)
65
+ const valid = ref(false)
61
66
  const emailRules = [
62
67
  (value) => {
63
68
  if (value) {
@@ -1,65 +1,3 @@
1
- <template>
2
- <v-sheet
3
- v-if="props.show_dialog"
4
- :width="props.width + 'px'"
5
- class="screenshot_menu"
6
- border="md"
7
- >
8
- <v-card class="bg-primary pa-0">
9
- <v-card-title>
10
- <h3 class="mt-4">Take a screenshot</h3>
11
- </v-card-title>
12
- <v-card-text class="pa-0">
13
- <v-container>
14
- <v-row>
15
- <v-col cols="8" class="py-0">
16
- <v-text-field v-model="filename" label="File name"></v-text-field>
17
- </v-col>
18
- <v-col cols="4" class="py-0">
19
- <v-select
20
- v-model="output_extension"
21
- :items="output_extensions"
22
- label="Extension"
23
- required
24
- />
25
- </v-col>
26
- </v-row>
27
-
28
- <v-row>
29
- <v-col cols="12" class="py-0">
30
- <v-switch
31
- v-model="include_background"
32
- :disabled="output_extension !== 'png'"
33
- label="Include background"
34
- inset
35
- ></v-switch>
36
- </v-col>
37
- </v-row>
38
- </v-container>
39
- </v-card-text>
40
- <v-card-actions justify-center>
41
- <v-btn
42
- variant="outlined"
43
- color="white"
44
- text
45
- @click="emit('close')"
46
- class="ml-8 mb-4"
47
- >Close</v-btn
48
- >
49
- <v-btn
50
- variant="outlined"
51
- class="mb-4"
52
- :disabled="!filename || !output_extension"
53
- color="white"
54
- text
55
- @click="takeScreenshot()"
56
- >Screenshot</v-btn
57
- >
58
- </v-card-actions>
59
- </v-card>
60
- </v-sheet>
61
- </template>
62
-
63
1
  <script setup>
64
2
  import fileDownload from "js-file-download"
65
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
@@ -107,12 +45,50 @@
107
45
  }
108
46
  })
109
47
  </script>
48
+ <template>
49
+ <OptionCard
50
+ v-if="props.show_dialog"
51
+ title="Take a screenshot"
52
+ :width="props.width"
53
+ class="position-absolute"
54
+ style="z-index: 2; top: 90px; right: 55px"
55
+ >
56
+ <v-container>
57
+ <v-row>
58
+ <v-col cols="8" class="py-0">
59
+ <v-text-field v-model="filename" label="File name"></v-text-field>
60
+ </v-col>
61
+ <v-col cols="4" class="py-0">
62
+ <v-select
63
+ v-model="output_extension"
64
+ :items="output_extensions"
65
+ label="Extension"
66
+ required
67
+ />
68
+ </v-col>
69
+ </v-row>
110
70
 
111
- <style scoped>
112
- .screenshot_menu {
113
- position: absolute;
114
- z-index: 2;
115
- top: 90px;
116
- right: 55px;
117
- }
118
- </style>
71
+ <v-row>
72
+ <v-col cols="12" class="py-0">
73
+ <v-switch
74
+ v-model="include_background"
75
+ :disabled="output_extension !== 'png'"
76
+ label="Include background"
77
+ inset
78
+ ></v-switch>
79
+ </v-col>
80
+ </v-row>
81
+ </v-container>
82
+
83
+ <template #actions>
84
+ <v-btn variant="text" color="white" @click="emit('close')">Close</v-btn>
85
+ <v-btn
86
+ variant="outlined"
87
+ :disabled="!filename || !output_extension"
88
+ color="white"
89
+ @click="takeScreenshot()"
90
+ >Screenshot</v-btn
91
+ >
92
+ </template>
93
+ </OptionCard>
94
+ </template>
@@ -1,62 +1,61 @@
1
1
  <template>
2
- <v-stepper-content :step="step_index + 1">
3
- <v-row
4
- align="center"
5
- class="mb-4 py-2"
6
- @click="set_current_step(step_index)"
7
- >
8
- <v-col cols="auto" class="d-flex justify-center align-center">
9
- <v-icon
10
- v-if="current_step_index > step_index"
11
- icon="mdi-check-circle"
12
- color="grey"
13
- />
14
- <v-icon
15
- v-else-if="current_step_index == step_index"
16
- :icon="`mdi-numeric-${step_index + 1}-circle`"
17
- color="primary"
18
- />
19
- <v-icon
20
- v-else
21
- :icon="`mdi-numeric-${step_index + 1}-circle`"
22
- color="grey"
23
- />
24
- </v-col>
25
- <v-col>
26
- <p class="m-0 font-weight-bold">
27
- {{ steps[step_index].step_title }}
28
- </p>
29
- </v-col>
30
- <v-chip-group
31
- v-if="
32
- steps[step_index].chips.length && current_step_index >= step_index
33
- "
34
- column
35
- class="d-flex flex-wrap ma-2 overflow-y-auto"
36
- multiple
37
- style="max-height: 150px"
2
+ <v-stepper-vertical-item
3
+ :value="step_index + 1"
4
+ :editable="step_index < current_step_index"
5
+ color="primary"
6
+ hide-actions
7
+ >
8
+ <template #title>
9
+ <v-sheet
10
+ color="transparent"
11
+ class="d-flex flex-column justify-center ps-2"
38
12
  >
39
- <v-chip
40
- v-for="(chip, chip_index) in steps[step_index].chips"
41
- :key="chip_index"
42
- class="ma-1"
43
- :title="chip"
13
+ <v-text
14
+ tag="h3"
15
+ class="text-subtitle-1 font-weight-bold mb-0 transition-swing"
16
+ :class="
17
+ current_step_index === step_index
18
+ ? 'text-primary'
19
+ : 'text-grey-darken-1'
20
+ "
44
21
  >
45
- {{ truncate(chip, 50) }}
46
- </v-chip>
47
- </v-chip-group>
48
- </v-row>
49
- <component
50
- v-if="step_index == current_step_index"
51
- :key="step_index"
52
- :is="steps[step_index].component.component_name"
53
- v-bind="steps[step_index].component.component_options"
54
- @increment_step="increment_step"
55
- @decrement_step="decrement_step"
56
- @update_values="update_values_event"
57
- @reset_values="$emit('reset_values')"
58
- />
59
- </v-stepper-content>
22
+ {{ steps[step_index].step_title }}
23
+ </v-text>
24
+
25
+ <v-sheet
26
+ v-if="sortedChips.length && current_step_index >= step_index"
27
+ color="transparent"
28
+ class="d-flex flex-wrap mt-2"
29
+ >
30
+ <v-chip
31
+ v-for="(chip, chip_index) in sortedChips"
32
+ :key="chip_index"
33
+ size="small"
34
+ class="me-2 mb-1 font-weight-medium"
35
+ color="primary"
36
+ variant="tonal"
37
+ >
38
+ {{ truncate(chip, 30) }}
39
+ </v-chip>
40
+ </v-sheet>
41
+ </v-sheet>
42
+ </template>
43
+
44
+ <v-card-text class="pt-0">
45
+ <v-divider class="mb-6 opacity-10" />
46
+
47
+ <component
48
+ v-if="step_index === current_step_index"
49
+ :key="step_index"
50
+ :is="steps[step_index].component.component_name"
51
+ v-bind="steps[step_index].component.component_options"
52
+ @increment_step="increment_step"
53
+ @decrement_step="decrement_step"
54
+ @update_values="update_values_event"
55
+ @reset_values="$emit('reset_values')"
56
+ />
57
+ </v-card-text>
58
+ </v-stepper-vertical-item>
60
59
  </template>
61
60
 
62
61
  <script setup>
@@ -74,9 +73,11 @@
74
73
  const stepper_tree = inject("stepper_tree")
75
74
  const { current_step_index, steps } = toRefs(stepper_tree)
76
75
 
77
- function set_current_step(step_index) {
78
- stepper_tree.current_step_index = step_index
79
- }
76
+ watch(current_step_index, (newVal, oldVal) => {
77
+ if (newVal < oldVal) {
78
+ stepper_tree.navigating_back = true
79
+ }
80
+ })
80
81
 
81
82
  function update_values_event(keys_values_object) {
82
83
  for (const [key, value] of Object.entries(keys_values_object)) {
@@ -1,20 +1,30 @@
1
1
  <template>
2
+ <v-card-item class="flex-shrink-0 pa-0">
3
+ <v-card-title
4
+ class="text-h4 text-primary px-8 pt-6 pb-4 font-weight-bold d-flex align-center"
5
+ >
6
+ <v-icon icon="mdi-file-upload-outline" class="mr-3" />
7
+ Import Data
8
+ </v-card-title>
9
+
10
+ <v-card-subtitle class="px-8 pb-4 text-medium">
11
+ Choose a file to import.
12
+ </v-card-subtitle>
13
+ </v-card-item>
14
+
2
15
  <v-stepper-vertical
3
- v-model="current_step_index"
4
- class="pa-4 ma-0"
5
- elevation="1"
6
- rounded
16
+ :model-value="current_step_index + 1"
17
+ @update:model-value="current_step_index = $event - 1"
18
+ flat
19
+ non-linear
20
+ class="pa-0 ma-0 bg-white rounded-xl overflow-hidden"
7
21
  >
8
- <v-stepper-items>
9
- <v-col cols="12">
10
- <Step
11
- v-for="(step, index) in steps"
12
- :key="step"
13
- :step_index="index"
14
- @reset_values="emit('reset_values')"
15
- />
16
- </v-col>
17
- </v-stepper-items>
22
+ <Step
23
+ v-for="(step, index) in steps"
24
+ :key="index"
25
+ :step_index="index"
26
+ @reset_values="emit('reset_values')"
27
+ />
18
28
  </v-stepper-vertical>
19
29
  </template>
20
30
 
@@ -1,50 +1,3 @@
1
- <template>
2
- <v-sheet class="menu-item-container transition-swing" color="transparent">
3
- <v-tooltip
4
- :location="props.itemProps.tooltip_location"
5
- :origin="props.itemProps.tooltip_origin"
6
- >
7
- <template v-slot:activator="{ props: tooltipProps }">
8
- <v-btn
9
- icon
10
- :active="is_active"
11
- @click.stop="toggleOptions"
12
- v-bind="tooltipProps"
13
- class="menu-btn bg-white border"
14
- elevation="2"
15
- >
16
- <v-img :src="btn_image" height="28" width="28" />
17
- </v-btn>
18
- </template>
19
- <span>{{ props.tooltip }}</span>
20
- </v-tooltip>
21
-
22
- <v-sheet
23
- v-if="is_active"
24
- ref="optionsRef"
25
- class="menu-options d-flex align-center pa-0"
26
- :class="optionsClass"
27
- :style="optionsStyle"
28
- color="transparent"
29
- @click.stop
30
- >
31
- <v-card
32
- @click.stop
33
- :title="props.tooltip"
34
- class="options-card rounded-xl border-thin elevation-24"
35
- width="320"
36
- :max-height="maxCardHeight"
37
- :ripple="false"
38
- theme="dark"
39
- >
40
- <v-card-text class="pa-5">
41
- <slot name="options" />
42
- </v-card-text>
43
- </v-card>
44
- </v-sheet>
45
- </v-sheet>
46
- </template>
47
-
48
1
  <script setup>
49
2
  import { useTheme } from "vuetify"
50
3
  import { useMenuStore } from "@ogw_front/stores/menu"
@@ -103,6 +56,46 @@
103
56
 
104
57
  const toggleOptions = () => menuStore.toggleItemOptions(props.index)
105
58
  </script>
59
+ <template>
60
+ <v-sheet class="menu-item-container transition-swing" color="transparent">
61
+ <v-tooltip
62
+ :location="props.itemProps.tooltip_location"
63
+ :origin="props.itemProps.tooltip_origin"
64
+ >
65
+ <template v-slot:activator="{ props: tooltipProps }">
66
+ <v-btn
67
+ icon
68
+ :active="is_active"
69
+ @click.stop="toggleOptions"
70
+ v-bind="tooltipProps"
71
+ class="menu-btn bg-white border"
72
+ elevation="2"
73
+ >
74
+ <v-img :src="btn_image" height="28" width="28" />
75
+ </v-btn>
76
+ </template>
77
+ <span>{{ props.tooltip }}</span>
78
+ </v-tooltip>
79
+
80
+ <v-sheet
81
+ v-if="is_active"
82
+ ref="optionsRef"
83
+ class="menu-options d-flex align-center pa-0"
84
+ :class="optionsClass"
85
+ :style="optionsStyle"
86
+ color="transparent"
87
+ @click.stop
88
+ >
89
+ <OptionCard
90
+ :title="props.tooltip"
91
+ width="320"
92
+ :max-height="maxCardHeight"
93
+ >
94
+ <slot name="options" />
95
+ </OptionCard>
96
+ </v-sheet>
97
+ </v-sheet>
98
+ </template>
106
99
 
107
100
  <style scoped>
108
101
  .menu-item-container {
@@ -146,22 +139,8 @@
146
139
  .options-right {
147
140
  left: 60px;
148
141
  }
142
+
149
143
  .options-left {
150
144
  right: 60px;
151
145
  }
152
-
153
- .options-card {
154
- background-color: rgba(30, 30, 30, 0.85) !important;
155
- backdrop-filter: blur(20px);
156
- -webkit-backdrop-filter: blur(20px);
157
- border-color: rgba(255, 255, 255, 0.15) !important;
158
- }
159
-
160
- ::v-deep(.v-list-item:hover) {
161
- background-color: rgba(255, 255, 255, 0.1);
162
- }
163
- ::v-deep(.v-slider-track__fill),
164
- ::v-deep(.v-selection-control--dirty .v-switch__track) {
165
- background-color: v-bind(primaryColor) !important;
166
- }
167
146
  </style>
@@ -1,75 +1,55 @@
1
- <template>
2
- <v-sheet
3
- :width="width + 'px'"
4
- class="z-scaling-menu"
5
- elevation="10"
6
- rounded="lg"
7
- >
8
- <v-card class="bg-primary pa-4" elevation="0">
9
- <v-card-title class="d-flex justify-space-between align-center">
10
- <h3 class="text-h5 font-weight-bold">Z Scaling Control</h3>
11
- </v-card-title>
12
- <v-card-text class="pt-4">
13
- <v-container>
14
- <v-row>
15
- <v-col cols="12" class="py-2">
16
- <v-slider
17
- v-model="zScale"
18
- :min="1"
19
- :max="10"
20
- :step="0.2"
21
- label="Z Scale"
22
- thumb-label
23
- color="white"
24
- track-color="white"
25
- />
26
- </v-col>
27
- </v-row>
28
- <v-row>
29
- <v-col cols="12" class="py-2">
30
- <v-text-field
31
- v-model.number="zScale"
32
- type="number"
33
- label="Z Scale Value"
34
- outlined
35
- dense
36
- hide-details
37
- step="0.1"
38
- class="custom-number-input"
39
- :min="1"
40
- />
41
- </v-col>
42
- </v-row>
43
- </v-container>
44
- </v-card-text>
45
- <v-card-actions class="justify-center pb-4">
46
- <v-btn
47
- variant="text"
48
- color="white"
49
- @click="$emit('close')"
50
- class="px-4"
51
- >
52
- Close
53
- </v-btn>
54
- <v-btn
55
- variant="outlined"
56
- color="white"
57
- @click="$emit('close')"
58
- class="px-4"
59
- >
60
- Apply
61
- </v-btn>
62
- </v-card-actions>
63
- </v-card>
64
- </v-sheet>
65
- </template>
66
-
67
1
  <script setup>
68
2
  const zScale = defineModel({ type: Number, default: 1 })
69
3
  const props = defineProps({
70
4
  width: { type: Number, default: 400 },
71
5
  })
72
6
  </script>
7
+ <template>
8
+ <OptionCard
9
+ title="Z Scaling Control"
10
+ :width="width"
11
+ class="position-absolute rounded-xl"
12
+ style="z-index: 2; top: 90px; right: 55px"
13
+ >
14
+ <v-container>
15
+ <v-row>
16
+ <v-col cols="12" class="py-2">
17
+ <v-slider
18
+ v-model="zScale"
19
+ :min="1"
20
+ :max="10"
21
+ :step="0.2"
22
+ label="Z Scale"
23
+ thumb-label
24
+ color="white"
25
+ track-color="white"
26
+ />
27
+ </v-col>
28
+ </v-row>
29
+ <v-row>
30
+ <v-col cols="12" class="py-2">
31
+ <v-text-field
32
+ v-model.number="zScale"
33
+ type="number"
34
+ label="Z Scale Value"
35
+ outlined
36
+ dense
37
+ hide-details
38
+ step="0.1"
39
+ :min="1"
40
+ />
41
+ </v-col>
42
+ </v-row>
43
+ </v-container>
44
+
45
+ <template #actions>
46
+ <v-btn variant="text" color="white" @click="$emit('close')">Close</v-btn>
47
+ <v-btn variant="outlined" color="white" @click="$emit('close')"
48
+ >Apply</v-btn
49
+ >
50
+ </template>
51
+ </OptionCard>
52
+ </template>
73
53
 
74
54
  <style scoped>
75
55
  .z-scaling-menu {
@@ -83,11 +63,4 @@
83
63
  .custom-number-input :deep(.v-input__control) {
84
64
  min-height: 48px;
85
65
  }
86
-
87
- .v-btn {
88
- border-radius: 8px;
89
- text-transform: none;
90
- font-weight: 500;
91
- letter-spacing: normal;
92
- }
93
66
  </style>
@@ -10,15 +10,19 @@ const autoStoreRegister = ({ store }) => {
10
10
  console.log(`[AutoRegister] Store "${store.$id}" processed`)
11
11
  }
12
12
 
13
- export default defineNuxtPlugin((nuxtApp) => {
14
- const { $pinia } = nuxtApp
15
- if (!$pinia) {
16
- console.warn("Pinia instance not available.")
17
- return
18
- }
13
+ export default defineNuxtPlugin({
14
+ name: "auto-store-register",
15
+ dependsOn: ["pinia"],
16
+ setup(nuxtApp) {
17
+ const { $pinia } = nuxtApp
18
+ if (!$pinia) {
19
+ console.warn("Pinia instance not available.")
20
+ return
21
+ }
19
22
 
20
- $pinia.use(autoStoreRegister)
21
- console.log(
22
- "[AUTOREGISTER PLUGIN] Loaded automatically from OpenGeodeWeb-Front",
23
- )
23
+ $pinia.use(autoStoreRegister)
24
+ console.log(
25
+ "[AUTOREGISTER PLUGIN] Loaded automatically from OpenGeodeWeb-Front",
26
+ )
27
+ },
24
28
  })
@@ -103,7 +103,7 @@ export const useDataStore = defineStore("data", () => {
103
103
 
104
104
  async function addModelComponents(values) {
105
105
  if (!values || values.length === 0) {
106
- console.warn("[addModelComponents] No mesh components to add")
106
+ console.debug("[addModelComponents] No mesh components to add")
107
107
  return
108
108
  }
109
109
  values.map((value) => {
@@ -1,4 +1,4 @@
1
1
  export const modelComponentsTable = {
2
2
  name: "model_components",
3
- schema: "[id+geode_id], viewer_id, type, name, created_at",
3
+ schema: "[id+geode_id], [id+type], viewer_id, type, name, created_at",
4
4
  }
@@ -32,6 +32,9 @@ export function useModelBlocksStyle() {
32
32
  modelBlockStyle(id, block_id).visibility = visibility
33
33
  }
34
34
  async function setModelBlocksVisibility(id, block_ids, visibility) {
35
+ if (!block_ids || block_ids.length === 0) {
36
+ return
37
+ }
35
38
  const blocks_viewer_ids = await dataStore.getMeshComponentsViewerIds(
36
39
  id,
37
40
  block_ids,
@@ -70,6 +73,9 @@ export function useModelBlocksStyle() {
70
73
  }
71
74
 
72
75
  async function setModelBlocksColor(id, block_ids, color) {
76
+ if (!block_ids || block_ids.length === 0) {
77
+ return
78
+ }
73
79
  const blocks_viewer_ids = await dataStore.getMeshComponentsViewerIds(
74
80
  id,
75
81
  block_ids,
@@ -32,6 +32,9 @@ export function useModelCornersStyle() {
32
32
  modelCornerStyle(id, corner_id).visibility = visibility
33
33
  }
34
34
  async function setModelCornersVisibility(id, corner_ids, visibility) {
35
+ if (!corner_ids || corner_ids.length === 0) {
36
+ return
37
+ }
35
38
  const corner_viewer_ids = await dataStore.getMeshComponentsViewerIds(
36
39
  id,
37
40
  corner_ids,
@@ -71,6 +74,9 @@ export function useModelCornersStyle() {
71
74
  }
72
75
 
73
76
  async function setModelCornersColor(id, corner_ids, color) {
77
+ if (!corner_ids || corner_ids.length === 0) {
78
+ return
79
+ }
74
80
  const corner_viewer_ids = await dataStore.getMeshComponentsViewerIds(
75
81
  id,
76
82
  corner_ids,
@@ -32,6 +32,9 @@ export function useModelLinesStyle() {
32
32
  modelLineStyle(id, line_id).visibility = visibility
33
33
  }
34
34
  async function setModelLinesVisibility(id, line_ids, visibility) {
35
+ if (!line_ids || line_ids.length === 0) {
36
+ return
37
+ }
35
38
  const line_viewer_ids = await dataStore.getMeshComponentsViewerIds(
36
39
  id,
37
40
  line_ids,
@@ -69,6 +72,9 @@ export function useModelLinesStyle() {
69
72
  modelLineStyle(id, line_id).color = color
70
73
  }
71
74
  async function setModelLinesColor(id, line_ids, color) {
75
+ if (!line_ids || line_ids.length === 0) {
76
+ return
77
+ }
72
78
  const line_viewer_ids = await dataStore.getMeshComponentsViewerIds(
73
79
  id,
74
80
  line_ids,
@@ -31,6 +31,9 @@ export function useModelSurfacesStyle() {
31
31
  modelSurfaceStyle(id, surface_id).visibility = visibility
32
32
  }
33
33
  async function setModelSurfacesVisibility(id, surface_ids, visibility) {
34
+ if (!surface_ids || surface_ids.length === 0) {
35
+ return
36
+ }
34
37
  const surface_viewer_ids = await dataStore.getMeshComponentsViewerIds(
35
38
  id,
36
39
  surface_ids,
@@ -68,6 +71,9 @@ export function useModelSurfacesStyle() {
68
71
  }
69
72
 
70
73
  async function setModelSurfacesColor(id, surface_ids, color) {
74
+ if (!surface_ids || surface_ids.length === 0) {
75
+ return
76
+ }
71
77
  const surface_viewer_ids = await dataStore.getMeshComponentsViewerIds(
72
78
  id,
73
79
  surface_ids,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@geode/opengeodeweb-front",
3
3
  "description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
4
4
  "type": "module",
5
- "version": "10.2.0",
5
+ "version": "10.2.1-rc.2",
6
6
  "main": "./nuxt.config.js",
7
7
  "scripts": {
8
8
  "lint": "eslint --fix --ext .js,.vue --ignore-path .gitignore .",
@@ -14,8 +14,8 @@
14
14
  "build": ""
15
15
  },
16
16
  "dependencies": {
17
- "@geode/opengeodeweb-back": "latest",
18
- "@geode/opengeodeweb-viewer": "latest",
17
+ "@geode/opengeodeweb-back": "next",
18
+ "@geode/opengeodeweb-viewer": "next",
19
19
  "@kitware/vtk.js": "33.3.0",
20
20
  "@mdi/font": "7.4.47",
21
21
  "@pinia/nuxt": "0.11.3",
@@ -5,4 +5,4 @@
5
5
  # pip-compile --output-file=tests/integration/microservices/back/requirements.txt tests/integration/microservices/back/requirements.in
6
6
  #
7
7
 
8
- opengeodeweb-back==6.*,>=6.0.0rc2
8
+ opengeodeweb-back==6.*,>=6.0.0
@@ -5,4 +5,4 @@
5
5
  # pip-compile --output-file=tests/integration/microservices/viewer/requirements.txt tests/integration/microservices/viewer/requirements.in
6
6
  #
7
7
 
8
- opengeodeweb-viewer==1.*,>=1.15.0rc2
8
+ opengeodeweb-viewer==1.*,>=1.15.0
@@ -46,11 +46,13 @@ describe("FileSelector", async () => {
46
46
  handler: () => ({}),
47
47
  })
48
48
 
49
- const v_file_input = file_uploader.findComponent(components.VFileInput)
50
- await v_file_input.trigger("click")
49
+ const v_file_input = file_uploader.find('input[type="file"]')
51
50
  const files = [new File(["fake_file"], "fake_file.txt")]
52
51
  const auto_upload = false
53
- await v_file_input.setValue(files)
52
+ Object.defineProperty(v_file_input.element, "files", {
53
+ value: files,
54
+ writable: true,
55
+ })
54
56
  await v_file_input.trigger("change")
55
57
  const v_btn = wrapper.findComponent(components.VBtn)
56
58
  await v_btn.trigger("click")
@@ -43,10 +43,11 @@ describe("FileUploader", async () => {
43
43
  props: { multiple: false, accept: "*.txt" },
44
44
  })
45
45
 
46
- const v_file_input = wrapper.findComponent(components.VFileInput)
47
- await v_file_input.trigger("click")
48
-
49
- await v_file_input.setValue(files)
46
+ const v_file_input = wrapper.find('input[type="file"]')
47
+ Object.defineProperty(v_file_input.element, "files", {
48
+ value: files,
49
+ writable: true,
50
+ })
50
51
  await v_file_input.trigger("change")
51
52
  const v_btn = wrapper.findComponent(components.VBtn)
52
53
 
@@ -55,10 +55,12 @@ describe("MissingFilesSelector", async () => {
55
55
  const file_uploader = wrapper.findComponent(FileUploader)
56
56
  expect(file_uploader.exists()).toBe(true)
57
57
 
58
- const v_file_input = file_uploader.findComponent(components.VFileInput)
59
- await v_file_input.trigger("click")
58
+ const v_file_input = file_uploader.find('input[type="file"]')
60
59
  const files = [new File(["fake_file"], "fake_file.txt")]
61
- await v_file_input.setValue(files)
60
+ Object.defineProperty(v_file_input.element, "files", {
61
+ value: files,
62
+ writable: true,
63
+ })
62
64
  await v_file_input.trigger("change")
63
65
  const v_btn = file_uploader.findComponent(components.VBtn)
64
66