Skip to content

Commit 9f04ed6

Browse files
- CHG: Better sorting of data.
- ADD: Added option to discard any local changes since last synchronization. -
1 parent f349f22 commit 9f04ed6

20 files changed

Lines changed: 482 additions & 116 deletions

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ declare module 'vue' {
172172
IBiconArrowLeftRight: typeof import('~icons/bi/con-arrow-left-right')['default']
173173
IBiCursorFill: typeof import('~icons/bi/cursor-fill')['default']
174174
IBiDatabaseFill: typeof import('~icons/bi/database-fill')['default']
175+
IBiDelete: typeof import('~icons/bi/delete')['default']
175176
IBiDiagram2: typeof import('~icons/bi/diagram2')['default']
176177
IBiDiamondHalf: typeof import('~icons/bi/diamond-half')['default']
177178
IBiDot: typeof import('~icons/bi/dot')['default']
@@ -293,6 +294,7 @@ declare module 'vue' {
293294
MissingTrialModal: typeof import('./src/components/modals/MissingTrialModal.vue')['default']
294295
MultiTraitTimeline: typeof import('./src/components/MultiTraitTimeline.vue')['default']
295296
OffscreenCanvas: typeof import('./src/components/canvas/OffscreenCanvas.vue')['default']
297+
PatternLock: typeof import('./src/components/canvas/PatternLock.vue')['default']
296298
PersonTypeIcon: typeof import('./src/components/icons/PersonTypeIcon.vue')['default']
297299
PlotCanvas: typeof import('./src/components/canvas/PlotCanvas.vue')['default']
298300
PlotCommentListModal: typeof import('./src/components/modals/PlotCommentListModal.vue')['default']

package-lock.json

Lines changed: 89 additions & 99 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/TraitDefinitionComponent.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
</template>
189189

190190
<script>
191+
import { mapState, mapStores } from 'pinia'
191192
import TraitImportExportGridScoreModal from '@/components/modals/TraitImportExportGridScoreModal.vue'
192193
import TraitImportTrialModal from '@/components/modals/TraitImportTrialModal.vue'
193194
import TraitImportExportGerminateModal from '@/components/modals/TraitImportExportGerminateModal.vue'
@@ -197,6 +198,7 @@ import { getTraitTypeText, isNumber } from '@/plugins/misc'
197198
import draggable from 'vuedraggable'
198199
import { getId } from '@/plugins/id'
199200
import { TRAIT_TIMEFRAME_TYPE_SUGGEST, TRAIT_TIMEFRAME_TYPE_ENFORCE } from '@/plugins/constants'
201+
import { coreStore } from '@/store'
200202
201203
import emitter from 'tiny-emitter/instance'
202204
@@ -276,6 +278,10 @@ export default {
276278
}
277279
},
278280
computed: {
281+
...mapStores(coreStore),
282+
...mapState(coreStore, [
283+
'storeLocale',
284+
]),
279285
timeframeCollapseVisible: function () {
280286
return this.newTrait.timeframe !== null
281287
},
@@ -301,7 +307,7 @@ export default {
301307
})
302308
303309
if (set.size > 0) {
304-
return [...set].sort((a, b) => a.localeCompare(b))
310+
return [...set].sort((a, b) => a.localeCompare(b, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' }))
305311
} else {
306312
return []
307313
}

src/components/TrialSelector.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,14 @@ export default {
200200
modalToShow: null,
201201
sorting: {
202202
latestUpdate: (a, b) => this.sortDescending ? (new Date(b.updatedOn) - new Date(a.updatedOn)) : (new Date(a.updatedOn) - new Date(b.updatedOn)),
203-
name: (a, b) => this.sortDescending ? b.name.localeCompare(a.name) : a.name.localeCompare(b.name),
203+
name: (a, b) => this.sortDescending ? b.name.localeCompare(a.name, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' }) : a.name.localeCompare(b.name, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' }),
204204
localVsRemote: (a, b) => {
205205
let result = 0
206206
const aExists = a.remoteUrl !== undefined && a.remoteUrl !== null
207207
const bExists = b.remoteUrl !== undefined && b.remoteUrl !== null
208208
209209
if ((!aExists && !bExists) || (aExists && bExists)) {
210-
result = a.name.localeCompare(b.name)
210+
result = a.name.localeCompare(b.name, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' })
211211
} else if (!aExists) {
212212
result = 1
213213
} else if (!bExists) {
@@ -226,6 +226,7 @@ export default {
226226
computed: {
227227
...mapStores(coreStore),
228228
...mapState(coreStore, [
229+
'storeLocale',
229230
'storeSelectedTrial',
230231
'storeTrialListMode',
231232
'storeTrialListArrangement'
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
<template>
2+
<div>
3+
<div>{{ pattern }}</div>
4+
<svg :class="`patternlock ${success === true ? 'success' : (success === false ? 'error' : '')}`" ref="svg" id="lock" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
5+
<g ref="lockActives" class="lock-actives"></g>
6+
<g ref="lockLines" class="lock-lines"></g>
7+
<g class="lock-dots">
8+
<circle cx="20" cy="20" r="2"/>
9+
<circle cx="50" cy="20" r="2"/>
10+
<circle cx="80" cy="20" r="2"/>
11+
12+
<circle cx="20" cy="50" r="2"/>
13+
<circle cx="50" cy="50" r="2"/>
14+
<circle cx="80" cy="50" r="2"/>
15+
16+
<circle cx="20" cy="80" r="2"/>
17+
<circle cx="50" cy="80" r="2"/>
18+
<circle cx="80" cy="80" r="2"/>
19+
</g>
20+
</svg>
21+
</div>
22+
</template>
23+
24+
<script lang="ts" setup>
25+
// Props
26+
const props = defineProps<{
27+
correctPattern: number[]
28+
}>()
29+
30+
const svgns = 'http://www.w3.org/2000/svg'
31+
const moveEvent = ['touchmove', 'mousemove']
32+
const start = ['touchstart', 'mousedown']
33+
34+
const success = ref<boolean | undefined>()
35+
const code = ref<SVGCircleElement[]>([])
36+
const svg = useTemplateRef('svg')
37+
const lines = useTemplateRef('lockLines')
38+
const actives = useTemplateRef('lockActives')
39+
40+
let dots: SVGCircleElement[] = []
41+
let pt: DOMPoint
42+
let currentLine: SVGLineElement | undefined
43+
let currentHandler: EventListener | undefined
44+
45+
const pattern: ComputedRef<number[]> = computed(() => {
46+
if (code.value) {
47+
return code.value.map(i => dots.indexOf(i) + 1)
48+
} else {
49+
return []
50+
}
51+
})
52+
53+
function vibrate() {
54+
// @ts-ignore
55+
if (navigator.vibrate) {
56+
window.navigator.vibrate(25)
57+
}
58+
}
59+
60+
function clear() {
61+
code.value = []
62+
currentLine = undefined
63+
currentHandler = undefined
64+
success.value = undefined
65+
if (lines.value) {
66+
lines.value.innerHTML = ''
67+
}
68+
if (actives.value) {
69+
actives.value.innerHTML = ''
70+
}
71+
}
72+
73+
function end() {
74+
stopTrack(currentLine)
75+
if (currentLine) {
76+
currentLine.remove()
77+
}
78+
moveEvent.forEach(ev => svg.value?.removeEventListener(ev, discoverDot))
79+
80+
if (props.correctPattern && props.correctPattern.length > 0) {
81+
success.value = props.correctPattern.join(',') === pattern.value.join(',')
82+
}
83+
}
84+
85+
function stopTrack(line: SVGLineElement | undefined, target?: SVGCircleElement) {
86+
if (line === undefined) {
87+
return
88+
}
89+
moveEvent.forEach(ev => {
90+
if (currentHandler) {
91+
svg.value?.removeEventListener(ev, currentHandler)
92+
}
93+
})
94+
if (target === undefined) {
95+
return
96+
}
97+
const x = target.getAttribute('cx')
98+
const y = target.getAttribute('cy')
99+
line.setAttribute('x2', `${x}`)
100+
line.setAttribute('y2', `${y}`)
101+
}
102+
103+
function beginTrack(target: SVGCircleElement) {
104+
code.value.push(target)
105+
const x = target.getAttribute('cx')
106+
const y = target.getAttribute('cy')
107+
if (x && y) {
108+
const line = createNewLine(x, y)
109+
const marker = createNewMarker(x, y)
110+
actives.value?.appendChild(marker)
111+
currentHandler = updateLine(line)
112+
moveEvent.forEach(ev => {
113+
if (currentHandler) {
114+
svg.value?.addEventListener(ev, currentHandler)
115+
}
116+
})
117+
lines.value?.appendChild(line)
118+
vibrate()
119+
return line
120+
} else {
121+
return undefined
122+
}
123+
}
124+
125+
function createNewMarker(x: string, y: string) {
126+
const marker = document.createElementNS(svgns, 'circle')
127+
marker.setAttribute('cx', x)
128+
marker.setAttribute('cy', y)
129+
marker.setAttribute('r', '6')
130+
return marker
131+
}
132+
133+
function createNewLine(x1: string, y1: string, x2?: string, y2?: string) {
134+
const line = document.createElementNS(svgns, 'line')
135+
line.setAttribute('x1', x1)
136+
line.setAttribute('y1', y1)
137+
if (x2 === undefined || y2 == undefined) {
138+
line.setAttribute('x2', x1)
139+
line.setAttribute('y2', y1)
140+
} else {
141+
line.setAttribute('x2', x2)
142+
line.setAttribute('y2', y2)
143+
}
144+
return line
145+
}
146+
147+
function isUsed(target: SVGCircleElement) {
148+
for (let i = 0; i < code.value.length; i++) {
149+
if (code.value[i] === target) {
150+
return true
151+
}
152+
}
153+
return false
154+
}
155+
156+
function isAvailable(target: SVGCircleElement) {
157+
for (let i = 0; i < dots.length; i++) {
158+
if (dots[i] === target) {
159+
return true
160+
}
161+
}
162+
return false
163+
}
164+
165+
function updateLine(line: SVGLineElement) {
166+
return (e: Event) => {
167+
e.preventDefault()
168+
if (currentLine !== line) {
169+
return
170+
}
171+
let pos = svgPosition(e.target as SVGGraphicsElement, e)
172+
line.setAttribute('x2', `${pos.x}`)
173+
line.setAttribute('y2', `${pos.y}`)
174+
return false
175+
}
176+
}
177+
178+
function getMousePos(e: any) {
179+
return {
180+
x: e.clientX || e.originalEvent.touches[0].clientX,
181+
y :e.clientY || e.originalEvent.touches[0].clientY
182+
}
183+
}
184+
185+
function discoverDot(e: Event, target?: SVGCircleElement) {
186+
if (!target) {
187+
const { x, y } = getMousePos(e)
188+
target = document.elementFromPoint(x, y) as SVGCircleElement
189+
}
190+
if (target) {
191+
if (isAvailable(target) && !isUsed(target)) {
192+
stopTrack(currentLine, target)
193+
currentLine = beginTrack(target)
194+
}
195+
}
196+
}
197+
198+
function svgPosition(element: SVGGraphicsElement | null, e: Event) {
199+
let { x, y } = getMousePos(e)
200+
pt.x = x
201+
pt.y = y
202+
return pt.matrixTransform(element?.getScreenCTM()?.inverse())
203+
}
204+
205+
onMounted(() => {
206+
if (svg.value) {
207+
dots = Array.from(svg.value.querySelectorAll('circle'))
208+
pt = svg.value.createSVGPoint()
209+
210+
start.forEach(ev => svg.value?.addEventListener(ev, e => {
211+
clear()
212+
e.preventDefault()
213+
214+
moveEvent.forEach(eev => svg.value?.addEventListener(eev, discoverDot))
215+
const endEvent = e.type == 'touchstart' ? 'touchend' : 'mouseup'
216+
document.addEventListener(endEvent, () => end(), { once: true })
217+
}))
218+
}
219+
})
220+
</script>
221+
222+
<style>
223+
#lock {
224+
width: 100%;
225+
height: calc(100% - 15vh);
226+
padding-bottom: 12vh;
227+
min-height: 120px;
228+
}
229+
230+
svg.patternlock g.lock-lines line {
231+
stroke-width: 1.5;
232+
stroke: black;
233+
opacity: 0.5;
234+
}
235+
236+
svg.patternlock g.lock-dots circle {
237+
stroke: transparent;
238+
fill: black;
239+
stroke-width: 13.5;
240+
}
241+
242+
svg.patternlock g.lock-actives circle {
243+
fill: black;
244+
opacity: .4;
245+
animation: lock-activate-dot .15s 0s ease 1;
246+
transform-origin: center;
247+
}
248+
249+
svg.patternlock g.lock-lines line {
250+
stroke-width: 1.5;
251+
stroke-linecap: round;
252+
}
253+
254+
svg.patternlock.success g.lock-actives circle {
255+
fill: var(--bs-success);
256+
}
257+
258+
svg.patternlock.error g.lock-actives circle {
259+
fill: var(--bs-danger);
260+
}
261+
262+
@keyframes lock-activate-dot {
263+
0% {
264+
transform: scale(0);
265+
}
266+
75% {
267+
transform: scale(1.1);
268+
}
269+
100% {
270+
transform: scale(1.0);
271+
}
272+
}
273+
</style>

src/components/charts/GermplasmRepHeatmap.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,8 @@ export default {
487487
})
488488
})
489489
490-
this.reps = [...reps].sort((a, b) => a ? a.localeCompare(b) : -1)
491-
this.allGermplasm = [...Object.keys(germplasmMap)].sort((a, b) => a.localeCompare(b))
490+
this.reps = [...reps].sort((a, b) => a ? a.localeCompare(b, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' }) : -1)
491+
this.allGermplasm = [...Object.keys(germplasmMap)].sort((a, b) => a.localeCompare(b, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' }))
492492
this.germplasmMap = germplasmMap
493493
494494
let hasActualReps = Object.values(germplasmMap).filter(m => {

src/components/dropdowns/TraitDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<IBiDiamondHalf :style="{ transform: 'rotate(45deg)' }" width="1.3em" height="1.3em" v-if="store.canvasShape === CANVAS_SHAPE_SQUARE" /><IBiCircleHalf v-else /> <span class="d-none d-lg-inline-block">{{ $t('toolbarTraitVisibility') }}</span>
55
</template>
66
<b-dropdown-form>
7-
<b-form-group class="mb-0" :label="$t('formLabelTraitDropdownCutoffDate')" label-for="cutoff">
7+
<b-form-group class="mb-0" :label="$t('formLabelTraitDropdownCutoffDate')" label-for="cutoff" :title="$t('formDescriptionTraitDropdownCutoffDate')">
88
<b-input-group>
99
<b-form-input id="cutoff" type="date" v-model="traitCutoff" v-if="atLeastOneMultiTrait" />
1010
<b-button variant="danger" v-if="traitCutoff" @click="traitCutoff = undefined"><IBiSlashCircle /></b-button>

src/components/modals/PlotCommentListModal.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ export default {
102102
computed: {
103103
...mapStores(coreStore),
104104
...mapState(coreStore, [
105-
'storeSelectedTrial'
105+
'storeSelectedTrial',
106+
'storeLocale'
106107
]),
107108
orderByOptions: function () {
108109
return [{
@@ -136,7 +137,7 @@ export default {
136137
result = a.comment.timestamp.localeCompare(b.comment.timestamp)
137138
break
138139
case 'germplasm':
139-
result = a.germplasm.localeCompare(b.germplasm)
140+
result = a.germplasm.localeCompare(b.germplasm, this.storeLocale || 'en', { numeric: true, sensitivity: 'base' })
140141
break
141142
case 'row':
142143
result = a.displayRow - b.displayRow

0 commit comments

Comments
 (0)