mirror of https://github.com/Fabio286/antares.git
refactor: ts and composition api for base components
This commit is contained in:
parent
cc5910b88f
commit
ae377a6c3c
|
@ -46,70 +46,57 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeUnmount } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, PropType, useSlots } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseConfirmModal',
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
validator: (prop: string) => ['small', 'medium', '400', 'large'].includes(prop),
|
||||
default: 'small'
|
||||
},
|
||||
hideFooter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmText: String,
|
||||
cancelText: String
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: String as PropType<'small' | 'medium' | '400' | 'large'>,
|
||||
validator: (prop: string) => ['small', 'medium', '400', 'large'].includes(prop),
|
||||
default: 'small'
|
||||
},
|
||||
emits: ['confirm', 'hide'],
|
||||
setup (props, { slots, emit }) {
|
||||
const hasHeader = computed(() => !!slots.header);
|
||||
const hasBody = computed(() => !!slots.body);
|
||||
const hasDefault = computed(() => !!slots.default);
|
||||
const modalSizeClass = computed(() => {
|
||||
if (props.size === 'small')
|
||||
return 'modal-sm';
|
||||
if (props.size === '400')
|
||||
return 'modal-400';
|
||||
else if (props.size === 'large')
|
||||
return 'modal-lg';
|
||||
else return '';
|
||||
});
|
||||
hideFooter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmText: String,
|
||||
cancelText: String
|
||||
});
|
||||
const emit = defineEmits(['confirm', 'hide']);
|
||||
const slots = useSlots();
|
||||
|
||||
const confirmModal = () => {
|
||||
emit('confirm');
|
||||
hideModal();
|
||||
};
|
||||
const hasHeader = computed(() => !!slots.header);
|
||||
const hasBody = computed(() => !!slots.body);
|
||||
const hasDefault = computed(() => !!slots.default);
|
||||
const modalSizeClass = computed(() => {
|
||||
if (props.size === 'small')
|
||||
return 'modal-sm';
|
||||
if (props.size === '400')
|
||||
return 'modal-400';
|
||||
else if (props.size === 'large')
|
||||
return 'modal-lg';
|
||||
else return '';
|
||||
});
|
||||
|
||||
const hideModal = () => {
|
||||
emit('hide');
|
||||
};
|
||||
const confirmModal = () => {
|
||||
emit('confirm');
|
||||
hideModal();
|
||||
};
|
||||
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
hideModal();
|
||||
};
|
||||
const hideModal = () => {
|
||||
emit('hide');
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', onKey);
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
hideModal();
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKey);
|
||||
});
|
||||
window.addEventListener('keydown', onKey);
|
||||
|
||||
return {
|
||||
hasHeader,
|
||||
hasBody,
|
||||
hasDefault,
|
||||
modalSizeClass,
|
||||
confirmModal,
|
||||
hideModal,
|
||||
onKey
|
||||
};
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKey);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -15,67 +15,60 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseContextMenu',
|
||||
props: {
|
||||
contextEvent: MouseEvent
|
||||
},
|
||||
emits: ['close-context'],
|
||||
data () {
|
||||
return {
|
||||
contextSize: null,
|
||||
isBottom: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
position () {
|
||||
let topCord = 0;
|
||||
let leftCord = 0;
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, Ref, ref } from 'vue';
|
||||
|
||||
if (this.contextEvent) {
|
||||
const { clientY, clientX } = this.contextEvent;
|
||||
topCord = `${clientY + 2}px`;
|
||||
leftCord = `${clientX + 5}px`;
|
||||
const contextContent: Ref<HTMLDivElement> = ref(null);
|
||||
const contextSize: Ref<DOMRect> = ref(null);
|
||||
const isBottom: Ref<boolean> = ref(false);
|
||||
const props = defineProps<{contextEvent: MouseEvent}>();
|
||||
const emit = defineEmits(['close-context']);
|
||||
|
||||
if (this.contextSize) {
|
||||
if (clientY + (this.contextSize.height < 200 ? 200 : this.contextSize.height) + 5 >= window.innerHeight) {
|
||||
topCord = `${clientY + 3 - this.contextSize.height}px`;
|
||||
this.isBottom = true;
|
||||
}
|
||||
const position = computed(() => {
|
||||
let topCord = '0px';
|
||||
let leftCord = '0px';
|
||||
|
||||
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
||||
leftCord = `${clientX - this.contextSize.width}px`;
|
||||
}
|
||||
if (props.contextEvent) {
|
||||
const { clientY, clientX } = props.contextEvent;
|
||||
topCord = `${clientY + 2}px`;
|
||||
leftCord = `${clientX + 5}px`;
|
||||
|
||||
if (contextSize.value) {
|
||||
if (clientY + (contextSize.value.height < 200 ? 200 : contextSize.value.height) + 5 >= window.innerHeight) {
|
||||
topCord = `${clientY + 3 - contextSize.value.height}px`;
|
||||
isBottom.value = true;
|
||||
}
|
||||
|
||||
return {
|
||||
top: topCord,
|
||||
left: leftCord
|
||||
};
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
},
|
||||
mounted () {
|
||||
if (this.$refs.contextContent)
|
||||
this.contextSize = this.$refs.contextContent.getBoundingClientRect();
|
||||
},
|
||||
beforeUnmount () {
|
||||
window.removeEventListener('keydown', this.onKey);
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$emit('close-context');
|
||||
},
|
||||
onKey (e) {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
this.close();
|
||||
if (clientX + contextSize.value.width + 5 >= window.innerWidth)
|
||||
leftCord = `${clientX - contextSize.value.width}px`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
top: topCord,
|
||||
left: leftCord
|
||||
};
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
emit('close-context');
|
||||
};
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
close();
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', onKey);
|
||||
|
||||
onMounted(() => {
|
||||
if (contextContent.value)
|
||||
contextSize.value = contextContent.value.getBoundingClientRect();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKey);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseLoader'
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.empty {
|
||||
position: absolute;
|
||||
|
|
|
@ -1,95 +1,93 @@
|
|||
<template>
|
||||
<div id="map" class="map" />
|
||||
</template>
|
||||
<script>
|
||||
import L from 'leaflet';
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, PropType, Ref, ref } from 'vue';
|
||||
import * as L from 'leaflet';
|
||||
import {
|
||||
point,
|
||||
lineString,
|
||||
polygon
|
||||
} from '@turf/helpers';
|
||||
import { GeoJsonObject } from 'geojson';
|
||||
import { getArrayDepth } from 'common/libs/getArrayDepth';
|
||||
|
||||
export default {
|
||||
name: 'BaseMap',
|
||||
props: {
|
||||
points: [Object, Array],
|
||||
isMultiSpatial: Boolean
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
map: null,
|
||||
markers: [],
|
||||
center: null
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
if (this.isMultiSpatial) {
|
||||
for (const element of this.points)
|
||||
this.markers.push(this.getMarkers(element));
|
||||
}
|
||||
else {
|
||||
this.markers = this.getMarkers(this.points);
|
||||
interface Coordinates { x: number; y: number }
|
||||
|
||||
if (!Array.isArray(this.points))
|
||||
this.center = [this.points.y, this.points.x];
|
||||
}
|
||||
const props = defineProps({
|
||||
points: [Object, Array] as PropType<Coordinates | Coordinates[]>,
|
||||
isMultiSpatial: Boolean
|
||||
});
|
||||
const map: Ref<L.Map> = ref(null);
|
||||
const markers: Ref<GeoJsonObject | GeoJsonObject[]> = ref(null);
|
||||
const center: Ref<[number, number]> = ref(null);
|
||||
|
||||
this.map = L.map('map', {
|
||||
center: this.center || [0, 0],
|
||||
zoom: 15,
|
||||
minZoom: 1,
|
||||
attributionControl: false
|
||||
});
|
||||
|
||||
L.control.attribution({ prefix: '<b>Leaflet</b>' }).addTo(this.map);
|
||||
|
||||
const geoJsonObj = L.geoJSON(this.markers, {
|
||||
style: function () {
|
||||
return {
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
};
|
||||
},
|
||||
pointToLayer: function (feature, latlng) {
|
||||
return L.circleMarker(latlng, {
|
||||
radius: 7,
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
});
|
||||
}
|
||||
}).addTo(this.map);
|
||||
|
||||
const southWest = L.latLng(-90, -180);
|
||||
const northEast = L.latLng(90, 180);
|
||||
const bounds = L.latLngBounds(southWest, northEast);
|
||||
this.map.setMaxBounds(bounds);
|
||||
|
||||
if (!this.center) this.map.fitBounds(geoJsonObj.getBounds());
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <b>OpenStreetMap</b>'
|
||||
}).addTo(this.map);
|
||||
},
|
||||
methods: {
|
||||
getMarkers (points) {
|
||||
if (Array.isArray(points)) {
|
||||
if (getArrayDepth(points) === 1)
|
||||
return lineString(points.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
||||
else
|
||||
return polygon(points.map(arr => arr.reduce((acc, curr) => [...acc, [curr.x, curr.y]], [])));
|
||||
}
|
||||
else
|
||||
return point([points.x, points.y]);
|
||||
}
|
||||
const getMarkers = (points: Coordinates) => {
|
||||
if (Array.isArray(points)) {
|
||||
if (getArrayDepth(points) === 1)
|
||||
return lineString(points.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
||||
else
|
||||
return polygon(points.map(arr => arr.reduce((acc: Coordinates[], curr: Coordinates) => [...acc, [curr.x, curr.y]], [])));
|
||||
}
|
||||
else
|
||||
return point([points.x, points.y]);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.isMultiSpatial) {
|
||||
for (const element of props.points as Coordinates[])
|
||||
(markers.value as GeoJsonObject[]).push(getMarkers(element));
|
||||
}
|
||||
else {
|
||||
markers.value = getMarkers(props.points as Coordinates);
|
||||
|
||||
if (!Array.isArray(props.points))
|
||||
center.value = [props.points.y, props.points.x];
|
||||
}
|
||||
|
||||
map.value = L.map('map', {
|
||||
center: center.value || [0, 0],
|
||||
zoom: 15,
|
||||
minZoom: 1,
|
||||
attributionControl: false
|
||||
});
|
||||
|
||||
L.control.attribution({ prefix: '<b>Leaflet</b>' }).addTo(map.value);
|
||||
|
||||
const geoJsonObj = L.geoJSON((markers.value as GeoJsonObject), {
|
||||
style: function () {
|
||||
return {
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
};
|
||||
},
|
||||
pointToLayer: function (feature, latlng) {
|
||||
return L.circleMarker(latlng, {
|
||||
radius: 7,
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
});
|
||||
}
|
||||
}).addTo(map.value);
|
||||
|
||||
const southWest = L.latLng(-90, -180);
|
||||
const northEast = L.latLng(90, 180);
|
||||
const bounds = L.latLngBounds(southWest, northEast);
|
||||
map.value.setMaxBounds(bounds);
|
||||
|
||||
if (!center.value) map.value.fitBounds(geoJsonObj.getBounds());
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <b>OpenStreetMap</b>'
|
||||
}).addTo(map.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -14,64 +14,58 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseNotification',
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
data () {
|
||||
return {
|
||||
isExpanded: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
notificationStatus () {
|
||||
let className = '';
|
||||
let iconName = '';
|
||||
switch (this.status) {
|
||||
case 'success':
|
||||
className = 'toast-success';
|
||||
iconName = 'mdi-check';
|
||||
break;
|
||||
case 'error':
|
||||
className = 'toast-error';
|
||||
iconName = 'mdi-alert-rhombus';
|
||||
break;
|
||||
case 'warning':
|
||||
className = 'toast-warning';
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'primary':
|
||||
className = 'toast-primary';
|
||||
iconName = 'mdi-information-outline';
|
||||
break;
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
return { className, iconName };
|
||||
},
|
||||
isExpandable () {
|
||||
return this.message.length > 80;
|
||||
}
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
methods: {
|
||||
hideToast () {
|
||||
this.$emit('close');
|
||||
},
|
||||
toggleExpand () {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
}
|
||||
status: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
const isExpanded = ref(false);
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const notificationStatus = computed(() => {
|
||||
let className = '';
|
||||
let iconName = '';
|
||||
switch (props.status) {
|
||||
case 'success':
|
||||
className = 'toast-success';
|
||||
iconName = 'mdi-check';
|
||||
break;
|
||||
case 'error':
|
||||
className = 'toast-error';
|
||||
iconName = 'mdi-alert-rhombus';
|
||||
break;
|
||||
case 'warning':
|
||||
className = 'toast-warning';
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'primary':
|
||||
className = 'toast-primary';
|
||||
iconName = 'mdi-information-outline';
|
||||
break;
|
||||
}
|
||||
|
||||
return { className, iconName };
|
||||
});
|
||||
|
||||
const isExpandable = computed(() => props.message.length > 80);
|
||||
|
||||
const hideToast = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const toggleExpand = () => {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toast {
|
||||
display: flex;
|
||||
|
|
|
@ -9,121 +9,112 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import * as ace from 'ace-builds';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import 'ace-builds/webpack-resolver';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
|
||||
export default {
|
||||
name: 'BaseTextEditor',
|
||||
props: {
|
||||
modelValue: String,
|
||||
mode: { type: String, default: 'text' },
|
||||
editorClass: { type: String, default: '' },
|
||||
autoFocus: { type: Boolean, default: false },
|
||||
readOnly: { type: Boolean, default: false },
|
||||
showLineNumbers: { type: Boolean, default: true },
|
||||
height: { type: Number, default: 200 }
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup () {
|
||||
const settingsStore = useSettingsStore();
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
mode: { type: String, default: 'text' },
|
||||
editorClass: { type: String, default: '' },
|
||||
autoFocus: { type: Boolean, default: false },
|
||||
readOnly: { type: Boolean, default: false },
|
||||
showLineNumbers: { type: Boolean, default: true },
|
||||
height: { type: Number, default: 200 }
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const settingsStore = useSettingsStore();
|
||||
const mode = ref(props.mode);
|
||||
|
||||
const {
|
||||
editorTheme,
|
||||
editorFontSize,
|
||||
autoComplete,
|
||||
lineWrap
|
||||
} = storeToRefs(settingsStore);
|
||||
const {
|
||||
editorTheme,
|
||||
editorFontSize,
|
||||
autoComplete,
|
||||
lineWrap
|
||||
} = storeToRefs(settingsStore);
|
||||
|
||||
return {
|
||||
editorTheme,
|
||||
editorFontSize,
|
||||
autoComplete,
|
||||
lineWrap
|
||||
};
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
editor: null,
|
||||
id: uidGen()
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
mode () {
|
||||
if (this.editor)
|
||||
this.editor.session.setMode(`ace/mode/${this.mode}`);
|
||||
},
|
||||
editorTheme () {
|
||||
if (this.editor)
|
||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||
},
|
||||
editorFontSize () {
|
||||
const sizes = {
|
||||
small: '12px',
|
||||
medium: '14px',
|
||||
large: '16px'
|
||||
};
|
||||
let editor: ace.Ace.Editor;
|
||||
const id = uidGen();
|
||||
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
fontSize: sizes[this.editorFontSize]
|
||||
});
|
||||
}
|
||||
},
|
||||
autoComplete () {
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
enableLiveAutocompletion: this.autoComplete
|
||||
});
|
||||
}
|
||||
},
|
||||
lineWrap () {
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
wrap: this.lineWrap
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.editor = ace.edit(`editor-${this.id}`, {
|
||||
mode: `ace/mode/${this.mode}`,
|
||||
theme: `ace/theme/${this.editorTheme}`,
|
||||
value: this.modelValue || '',
|
||||
fontSize: '14px',
|
||||
printMargin: false,
|
||||
readOnly: this.readOnly,
|
||||
showLineNumbers: this.showLineNumbers,
|
||||
showGutter: this.showLineNumbers
|
||||
watch(mode, () => {
|
||||
if (editor)
|
||||
editor.session.setMode(`ace/mode/${props.mode}`);
|
||||
});
|
||||
|
||||
watch(editorTheme, () => {
|
||||
if (editor)
|
||||
editor.setTheme(`ace/theme/${editorTheme.value}`);
|
||||
});
|
||||
|
||||
watch(editorFontSize, () => {
|
||||
const sizes = {
|
||||
small: 12,
|
||||
medium: 14,
|
||||
large: 16
|
||||
};
|
||||
|
||||
if (editor) {
|
||||
editor.setOptions({
|
||||
fontSize: sizes[editorFontSize.value as undefined as 'small' | 'medium' | 'large']
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.editor.setOptions({
|
||||
enableBasicAutocompletion: false,
|
||||
wrap: this.lineWrap,
|
||||
enableSnippets: false,
|
||||
enableLiveAutocompletion: false
|
||||
watch(autoComplete, () => {
|
||||
if (editor) {
|
||||
editor.setOptions({
|
||||
enableLiveAutocompletion: autoComplete.value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.editor.session.on('change', () => {
|
||||
const content = this.editor.getValue();
|
||||
this.$emit('update:modelValue', content);
|
||||
watch(lineWrap, () => {
|
||||
if (editor) {
|
||||
editor.setOptions({
|
||||
wrap: lineWrap.value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (this.autoFocus) {
|
||||
setTimeout(() => {
|
||||
this.editor.focus();
|
||||
this.editor.resize();
|
||||
}, 20);
|
||||
}
|
||||
onMounted(() => {
|
||||
editor = ace.edit(`editor-${id}`, {
|
||||
mode: `ace/mode/${mode.value}`,
|
||||
theme: `ace/theme/${editorTheme.value}`,
|
||||
value: props.modelValue || '',
|
||||
fontSize: 14,
|
||||
printMargin: false,
|
||||
readOnly: props.readOnly,
|
||||
showLineNumbers: props.showLineNumbers,
|
||||
showGutter: props.showLineNumbers
|
||||
});
|
||||
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: false,
|
||||
wrap: lineWrap,
|
||||
enableSnippets: false,
|
||||
enableLiveAutocompletion: false
|
||||
});
|
||||
|
||||
editor.session.on('changeFold', () => {
|
||||
const content = editor.getValue();
|
||||
emit('update:modelValue', content);
|
||||
});
|
||||
|
||||
if (props.autoFocus) {
|
||||
setTimeout(() => {
|
||||
this.editor.resize();
|
||||
editor.focus();
|
||||
editor.resize();
|
||||
}, 20);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
editor.resize();
|
||||
}, 20);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -9,67 +9,64 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseToast',
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
data () {
|
||||
return {
|
||||
isVisible: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toastStatus () {
|
||||
let className = '';
|
||||
let iconName = '';
|
||||
switch (this.status) {
|
||||
case 'success':
|
||||
className = 'toast-success';
|
||||
iconName = 'mdi-check';
|
||||
break;
|
||||
case 'error':
|
||||
className = 'toast-error';
|
||||
iconName = 'mdi-alert-rhombus';
|
||||
break;
|
||||
case 'warning':
|
||||
className = 'toast-warning';
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'primary':
|
||||
className = 'toast-primary';
|
||||
iconName = 'mdi-information-outline';
|
||||
break;
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { computed } from '@vue/reactivity';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
return { className, iconName };
|
||||
}
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
watch: {
|
||||
message: function () {
|
||||
if (this.message)
|
||||
this.isVisible = true;
|
||||
else
|
||||
this.isVisible = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideToast () {
|
||||
this.isVisible = false;
|
||||
this.$emit('close');
|
||||
}
|
||||
status: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const isVisible = ref(false);
|
||||
const message = ref(props.message);
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const toastStatus = computed(() => {
|
||||
let className = '';
|
||||
let iconName = '';
|
||||
switch (props.status) {
|
||||
case 'success':
|
||||
className = 'toast-success';
|
||||
iconName = 'mdi-check';
|
||||
break;
|
||||
case 'error':
|
||||
className = 'toast-error';
|
||||
iconName = 'mdi-alert-rhombus';
|
||||
break;
|
||||
case 'warning':
|
||||
className = 'toast-warning';
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'primary':
|
||||
className = 'toast-primary';
|
||||
iconName = 'mdi-information-outline';
|
||||
break;
|
||||
}
|
||||
|
||||
return { className, iconName };
|
||||
});
|
||||
|
||||
watch(message, () => {
|
||||
if (message.value)
|
||||
isVisible.value = true;
|
||||
else
|
||||
isVisible.value = false;
|
||||
});
|
||||
|
||||
const hideToast = () => {
|
||||
isVisible.value = false;
|
||||
emit('close');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toast {
|
||||
display: flex;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{{ lastPart(modelValue) }}
|
||||
</span>
|
||||
<i
|
||||
v-if="modelValue.length"
|
||||
v-if="modelValue"
|
||||
class="file-uploader-reset mdi mdi-close"
|
||||
@click.prevent="clear"
|
||||
/>
|
||||
|
@ -22,41 +22,35 @@
|
|||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseUploadInput',
|
||||
props: {
|
||||
message: {
|
||||
default: 'Browse',
|
||||
type: String
|
||||
},
|
||||
modelValue: {
|
||||
default: '',
|
||||
type: String
|
||||
}
|
||||
},
|
||||
emits: ['change', 'clear'],
|
||||
data () {
|
||||
return {
|
||||
id: null
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.id = this._uid;
|
||||
},
|
||||
methods: {
|
||||
clear () {
|
||||
this.$emit('clear');
|
||||
},
|
||||
lastPart (string) {
|
||||
if (!string) return '';
|
||||
<script setup lang="ts">
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
|
||||
string = string.split(/[/\\]+/).pop();
|
||||
if (string.length >= 19)
|
||||
string = `...${string.slice(-19)}`;
|
||||
return string;
|
||||
}
|
||||
defineProps({
|
||||
message: {
|
||||
default: 'Browse',
|
||||
type: String
|
||||
},
|
||||
modelValue: {
|
||||
default: '',
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change', 'clear']);
|
||||
|
||||
const id = uidGen();
|
||||
|
||||
const clear = () => {
|
||||
emit('clear');
|
||||
};
|
||||
|
||||
const lastPart = (string: string) => {
|
||||
if (!string) return '';
|
||||
|
||||
string = string.split(/[/\\]+/).pop();
|
||||
if (string.length >= 19)
|
||||
string = `...${string.slice(-19)}`;
|
||||
return string;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="vscroll-holder">
|
||||
<div ref="root" class="vscroll-holder">
|
||||
<div
|
||||
class="vscroll-spacer"
|
||||
:style="{
|
||||
|
@ -20,71 +20,77 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseVirtualScroll',
|
||||
props: {
|
||||
items: Array,
|
||||
itemHeight: Number,
|
||||
visibleHeight: Number,
|
||||
scrollElement: {
|
||||
type: HTMLDivElement,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
topHeight: 0,
|
||||
bottomHeight: 0,
|
||||
visibleItems: [],
|
||||
renderTimeout: null,
|
||||
localScrollElement: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
scrollElement () {
|
||||
this.setScrollElement();
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.setScrollElement();
|
||||
},
|
||||
beforeUnmount () {
|
||||
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||
},
|
||||
methods: {
|
||||
checkScrollPosition (e) {
|
||||
clearTimeout(this.renderTimeout);
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
|
||||
|
||||
this.renderTimeout = setTimeout(() => {
|
||||
this.updateWindow(e);
|
||||
}, 200);
|
||||
},
|
||||
updateWindow () {
|
||||
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
||||
const totalScrollHeight = this.items.length * this.itemHeight;
|
||||
const offset = 50;
|
||||
|
||||
const scrollTop = this.localScrollElement.scrollTop;
|
||||
|
||||
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight);
|
||||
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount;
|
||||
const firstCutIndex = Math.max(firstVisibleIndex - offset, 0);
|
||||
const lastCutIndex = lastVisibleIndex + offset;
|
||||
|
||||
this.visibleItems = this.items.slice(firstCutIndex, lastCutIndex);
|
||||
|
||||
this.topHeight = firstCutIndex * this.itemHeight;
|
||||
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
||||
},
|
||||
setScrollElement () {
|
||||
if (this.localScrollElement)
|
||||
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||
|
||||
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
||||
this.updateWindow();
|
||||
this.localScrollElement.addEventListener('scroll', this.checkScrollPosition);
|
||||
}
|
||||
const props = defineProps({
|
||||
items: Array,
|
||||
itemHeight: Number,
|
||||
visibleHeight: Number,
|
||||
scrollElement: {
|
||||
type: HTMLDivElement,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const root = ref(null);
|
||||
const topHeight: Ref<number> = ref(0);
|
||||
const bottomHeight: Ref<number> = ref(0);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const visibleItems: Ref<any[]> = ref([]);
|
||||
const renderTimeout: Ref<NodeJS.Timeout> = ref(null);
|
||||
const localScrollElement: Ref<HTMLDivElement> = ref(null);
|
||||
const scrollElement = ref(props.scrollElement);
|
||||
|
||||
const checkScrollPosition = () => {
|
||||
clearTimeout(renderTimeout.value);
|
||||
|
||||
renderTimeout.value = setTimeout(() => {
|
||||
updateWindow();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const updateWindow = () => {
|
||||
const visibleItemsCount = Math.ceil(props.visibleHeight / props.itemHeight);
|
||||
const totalScrollHeight = props.items.length * props.itemHeight;
|
||||
const offset = 50;
|
||||
|
||||
const scrollTop = localScrollElement.value.scrollTop;
|
||||
|
||||
const firstVisibleIndex = Math.floor(scrollTop / props.itemHeight);
|
||||
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount;
|
||||
const firstCutIndex = Math.max(firstVisibleIndex - offset, 0);
|
||||
const lastCutIndex = lastVisibleIndex + offset;
|
||||
|
||||
visibleItems.value = props.items.slice(firstCutIndex, lastCutIndex);
|
||||
|
||||
topHeight.value = firstCutIndex * props.itemHeight;
|
||||
bottomHeight.value = totalScrollHeight - visibleItems.value.length * props.itemHeight - topHeight.value;
|
||||
};
|
||||
|
||||
const setScrollElement = () => {
|
||||
if (localScrollElement.value)
|
||||
localScrollElement.value.removeEventListener('scroll', checkScrollPosition);
|
||||
|
||||
localScrollElement.value = scrollElement.value ? scrollElement.value : root.value;
|
||||
updateWindow();
|
||||
localScrollElement.value.addEventListener('scroll', checkScrollPosition);
|
||||
};
|
||||
|
||||
watch(scrollElement, () => {
|
||||
setScrollElement();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setScrollElement();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
localScrollElement.value.removeEventListener('scroll', checkScrollPosition);
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
updateWindow
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
>
|
||||
<BaseUploadInput
|
||||
v-else-if="inputProps().type === 'file'"
|
||||
:value="selectedValue"
|
||||
:model-value="selectedValue"
|
||||
:message="$t('word.browse')"
|
||||
@clear="clearValue"
|
||||
@change="filesChange($event)"
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import arrayToFile from '../libs/arrayToFile';
|
||||
import { arrayToFile } from '../libs/arrayToFile';
|
||||
import { useNotificationsStore } from '@/stores/notifications';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
import { useConnectionsStore } from '@/stores/connections';
|
||||
|
|
|
@ -328,7 +328,7 @@ import { storeToRefs } from 'pinia';
|
|||
import { useApplicationStore } from '@/stores/application';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import localesNames from '@/i18n/supported-locales';
|
||||
import { localesNames } from '@/i18n/supported-locales';
|
||||
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate';
|
||||
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog';
|
||||
import BaseTextEditor from '@/components/BaseTextEditor';
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.databasePath"
|
||||
:model-value="connection.databasePath"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('databasePath')"
|
||||
@change="pathSelection($event, 'databasePath')"
|
||||
|
@ -211,7 +211,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.key"
|
||||
:model-value="connection.key"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('key')"
|
||||
@change="pathSelection($event, 'key')"
|
||||
|
@ -224,7 +224,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.cert"
|
||||
:model-value="connection.cert"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('cert')"
|
||||
@change="pathSelection($event, 'cert')"
|
||||
|
@ -237,7 +237,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.ca"
|
||||
:model-value="connection.ca"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('ca')"
|
||||
@change="pathSelection($event, 'ca')"
|
||||
|
@ -342,7 +342,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.sshKey"
|
||||
:model-value="connection.sshKey"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('sshKey')"
|
||||
@change="pathSelection($event, 'sshKey')"
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.databasePath"
|
||||
:model-value="localConnection.databasePath"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('databasePath')"
|
||||
@change="pathSelection($event, 'databasePath')"
|
||||
|
@ -207,7 +207,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.key"
|
||||
:model-value="localConnection.key"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('key')"
|
||||
@change="pathSelection($event, 'key')"
|
||||
|
@ -220,7 +220,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.cert"
|
||||
:model-value="localConnection.cert"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('cert')"
|
||||
@change="pathSelection($event, 'cert')"
|
||||
|
@ -233,7 +233,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.ca"
|
||||
:model-value="localConnection.ca"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('ca')"
|
||||
@change="pathSelection($event, 'ca')"
|
||||
|
@ -330,7 +330,7 @@
|
|||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.sshKey"
|
||||
:model-value="localConnection.sshKey"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('sshKey')"
|
||||
@change="pathSelection($event, 'sshKey')"
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</label>
|
||||
</div>
|
||||
<div class="td p-0 type-int" tabindex="0">
|
||||
<template v-if="fieldType.length">
|
||||
<template v-if="fieldType?.length">
|
||||
<span
|
||||
v-if="!isInlineEditor.length"
|
||||
class="cell-content"
|
||||
|
@ -355,7 +355,7 @@ export default {
|
|||
},
|
||||
props: {
|
||||
row: Object,
|
||||
dataTypes: Array,
|
||||
dataTypes: { type: Array, default: () => [] },
|
||||
indexes: Array,
|
||||
foreigns: Array,
|
||||
customizations: Object
|
||||
|
|
|
@ -112,7 +112,7 @@ import { uidGen } from 'common/libs/uidGen';
|
|||
import { useNotificationsStore } from '@/stores/notifications';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import arrayToFile from '../libs/arrayToFile';
|
||||
import { arrayToFile } from '../libs/arrayToFile';
|
||||
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default {
|
||||
export const localesNames = {
|
||||
'en-US': 'English',
|
||||
'it-IT': 'Italiano',
|
||||
'ar-SA': 'العربية',
|
|
@ -29,7 +29,7 @@ createApp(App)
|
|||
.mount('#app');
|
||||
|
||||
const { locale } = useSettingsStore();
|
||||
i18n.global.locale = locale;
|
||||
i18n.global.locale = locale as string;// TODO: temp
|
||||
|
||||
// IPC exceptions
|
||||
ipcRenderer.on('unhandled-exception', (event, error) => {
|
||||
|
@ -59,7 +59,7 @@ ipcRenderer.on('no-auto-update', () => {
|
|||
|
||||
ipcRenderer.on('download-progress', (event, data) => {
|
||||
useApplicationStore().updateStatus = 'downloading';
|
||||
useApplicationStore().downloadprogress = data.percent;
|
||||
useApplicationStore().downloadProgress = data.percent;
|
||||
});
|
||||
|
||||
ipcRenderer.on('update-downloaded', () => {
|
|
@ -4,6 +4,9 @@ import Connection from '@/ipc-api/Connection';
|
|||
import Schema from '@/ipc-api/Schema';
|
||||
import Users from '@/ipc-api/Users';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
|
||||
import customizations from 'common/customizations';
|
||||
|
||||
import { useConnectionsStore } from '@/stores/connections';
|
||||
import { useNotificationsStore } from '@/stores/notifications';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
|
@ -22,14 +25,14 @@ export const useWorkspacesStore = defineStore('workspaces', {
|
|||
if (state.selectedWorkspace) return state.selectedWorkspace;
|
||||
return state.workspaces[0].uid;
|
||||
},
|
||||
getWorkspace: state => uid => {
|
||||
getWorkspace: state => (uid) => {
|
||||
return state.workspaces.find(workspace => workspace.uid === uid);
|
||||
},
|
||||
getDatabaseVariable: state => (uid, name) => {
|
||||
return state.workspaces.find(workspace => workspace.uid === uid).variables.find(variable => variable.name === name);
|
||||
},
|
||||
getWorkspaceTab (state) {
|
||||
return tUid => {
|
||||
return (tUid) => {
|
||||
if (!this.getSelected) return;
|
||||
const workspace = state.workspaces.find(workspace => workspace.uid === this.getSelected);
|
||||
if ('tabs' in workspace)
|
||||
|
@ -89,24 +92,24 @@ export const useWorkspacesStore = defineStore('workspaces', {
|
|||
else {
|
||||
let dataTypes = [];
|
||||
let indexTypes = [];
|
||||
let customizations = {};
|
||||
let clientCustomizations;
|
||||
|
||||
switch (connection.client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
dataTypes = require('common/data-types/mysql');
|
||||
indexTypes = require('common/index-types/mysql');
|
||||
customizations = require('common/customizations/mysql');
|
||||
dataTypes = require('common/data-types/mysql').default;
|
||||
indexTypes = require('common/index-types/mysql').default;
|
||||
clientCustomizations = customizations.mysql;
|
||||
break;
|
||||
case 'pg':
|
||||
dataTypes = require('common/data-types/postgresql');
|
||||
indexTypes = require('common/index-types/postgresql');
|
||||
customizations = require('common/customizations/postgresql');
|
||||
dataTypes = require('common/data-types/postgresql').default;
|
||||
indexTypes = require('common/index-types/postgresql').default;
|
||||
clientCustomizations = customizations.pg;
|
||||
break;
|
||||
case 'sqlite':
|
||||
dataTypes = require('common/data-types/sqlite');
|
||||
indexTypes = require('common/index-types/sqlite');
|
||||
customizations = require('common/customizations/sqlite');
|
||||
dataTypes = require('common/data-types/sqlite').default;
|
||||
indexTypes = require('common/index-types/sqlite').default;
|
||||
clientCustomizations = customizations.sqlite;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -144,7 +147,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
|
|||
client: connection.client,
|
||||
dataTypes,
|
||||
indexTypes,
|
||||
customizations,
|
||||
customizations: clientCustomizations,
|
||||
structure: response,
|
||||
connectionStatus: 'connected',
|
||||
tabs: cachedTabs,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
declare module '@/App.vue';
|
||||
declare module 'v-mask';
|
Loading…
Reference in New Issue