@xen-orchestra/web-core 0.10.0 → 0.11.0
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/lib/components/charts/LinearChart.md +33 -0
- package/lib/components/charts/LinearChart.vue +77 -0
- package/lib/components/connection-status/VtsConnectionStatus.vue +5 -1
- package/lib/components/console/VtsActionsConsole.vue +26 -12
- package/lib/components/console/VtsRemoteConsole.vue +58 -16
- package/lib/components/data-table/VtsDataTable.vue +58 -0
- package/lib/components/menu/MenuItem.vue +9 -13
- package/lib/components/menu/MenuList.vue +15 -13
- package/lib/components/tab/TabItem.vue +3 -4
- package/lib/components/tab/TabList.vue +5 -9
- package/lib/components/table/ColumnTitle.vue +1 -1
- package/lib/components/ui/account-menu-button/UiAccountMenuButton.vue +2 -3
- package/lib/components/ui/button/UiButton.vue +11 -17
- package/lib/components/ui/checkbox/UiCheckbox.vue +11 -15
- package/lib/components/ui/dropdown-button/UiDropdownButton.vue +9 -13
- package/lib/components/ui/input/UiInput.vue +1 -0
- package/lib/components/ui/radio-button/UiRadioButton.vue +12 -15
- package/lib/components/ui/toggle/UiToggle.vue +6 -10
- package/lib/composables/chart-theme.composable.ts +382 -0
- package/lib/composables/disabled.composable.ts +15 -0
- package/lib/composables/route-query/types.ts +3 -2
- package/lib/i18n.ts +53 -0
- package/lib/layouts/CoreLayout.vue +20 -13
- package/lib/locales/es.json +97 -0
- package/lib/types/chart.ts +9 -0
- package/lib/utils/injection-keys.util.ts +5 -0
- package/package.json +7 -2
- package/lib/context.ts +0 -10
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# LinearChart component
|
|
2
|
+
|
|
3
|
+
```vue
|
|
4
|
+
<template>
|
|
5
|
+
<LinearChart :data="data" :value-formatter="customValueFormatter" />
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import type { LinearChartData } from '@/types/chart'
|
|
10
|
+
import LinearChart from '@/components/charts/LinearChart.vue'
|
|
11
|
+
|
|
12
|
+
const data: LinearChartData = [
|
|
13
|
+
{
|
|
14
|
+
label: 'First series',
|
|
15
|
+
data: [
|
|
16
|
+
{ timestamp: 1670478371123, value: 1234 },
|
|
17
|
+
{ timestamp: 1670478519751, value: 1234 },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: 'Second series',
|
|
22
|
+
data: [
|
|
23
|
+
{ timestamp: 1670478519751, value: 1234 },
|
|
24
|
+
{ timestamp: 167047555000, value: 1234 },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const customValueFormatter = (value: number) => {
|
|
30
|
+
return `${value} (Doubled: ${value * 2})`
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<VueCharts :option autoresize class="chart" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts" setup>
|
|
6
|
+
import type { LinearChartData, ValueFormatter } from '@core/types/chart'
|
|
7
|
+
import { IK_CHART_VALUE_FORMATTER } from '@core/utils/injection-keys.util'
|
|
8
|
+
import { utcFormat } from 'd3-time-format'
|
|
9
|
+
import type { EChartsOption } from 'echarts'
|
|
10
|
+
import { LineChart } from 'echarts/charts'
|
|
11
|
+
import { GridComponent, LegendComponent, TooltipComponent } from 'echarts/components'
|
|
12
|
+
import { use } from 'echarts/core'
|
|
13
|
+
import { CanvasRenderer } from 'echarts/renderers'
|
|
14
|
+
import { computed, provide } from 'vue'
|
|
15
|
+
import VueCharts from 'vue-echarts'
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{
|
|
18
|
+
data: LinearChartData
|
|
19
|
+
valueFormatter?: ValueFormatter
|
|
20
|
+
maxValue?: number
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const Y_AXIS_MAX_VALUE = 200
|
|
24
|
+
|
|
25
|
+
const valueFormatter = computed<ValueFormatter>(() => {
|
|
26
|
+
const formatter = props.valueFormatter
|
|
27
|
+
|
|
28
|
+
return value => {
|
|
29
|
+
if (formatter === undefined) {
|
|
30
|
+
return value.toString()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return formatter(value)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
provide(IK_CHART_VALUE_FORMATTER, valueFormatter)
|
|
38
|
+
|
|
39
|
+
use([CanvasRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent])
|
|
40
|
+
|
|
41
|
+
const option = computed<EChartsOption>(() => ({
|
|
42
|
+
legend: {
|
|
43
|
+
data: props.data.map(series => series.label),
|
|
44
|
+
},
|
|
45
|
+
tooltip: {
|
|
46
|
+
valueFormatter: v => valueFormatter.value(v as number),
|
|
47
|
+
},
|
|
48
|
+
xAxis: {
|
|
49
|
+
type: 'time',
|
|
50
|
+
axisLabel: {
|
|
51
|
+
formatter: (timestamp: number) => utcFormat('%a\n%I:%M\n%p')(new Date(timestamp)),
|
|
52
|
+
showMaxLabel: false,
|
|
53
|
+
showMinLabel: false,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
yAxis: {
|
|
57
|
+
type: 'value',
|
|
58
|
+
axisLabel: {
|
|
59
|
+
formatter: valueFormatter.value,
|
|
60
|
+
},
|
|
61
|
+
max: props.maxValue ?? Y_AXIS_MAX_VALUE,
|
|
62
|
+
},
|
|
63
|
+
series: props.data.map((series, index) => ({
|
|
64
|
+
type: 'line',
|
|
65
|
+
name: series.label,
|
|
66
|
+
zlevel: index + 1,
|
|
67
|
+
data: series.data.map(item => [item.timestamp, item.value]),
|
|
68
|
+
})),
|
|
69
|
+
}))
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<style lang="postcss" scoped>
|
|
73
|
+
.chart {
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 30rem;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -9,7 +9,11 @@ import UiInfo, { type InfoAccent } from '@core/components/ui/info/UiInfo.vue'
|
|
|
9
9
|
import { computed, type ComputedRef } from 'vue'
|
|
10
10
|
import { useI18n } from 'vue-i18n'
|
|
11
11
|
|
|
12
|
-
type ConnectionStatus =
|
|
12
|
+
export type ConnectionStatus =
|
|
13
|
+
| 'connected'
|
|
14
|
+
| 'disconnected'
|
|
15
|
+
| 'partially-connected'
|
|
16
|
+
| 'disconnected-from-physical-device'
|
|
13
17
|
type ConnectionStatusesMap = Record<ConnectionStatus, { text: string; accent: InfoAccent }>
|
|
14
18
|
|
|
15
19
|
const { status } = defineProps<{
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<UiCardTitle>{{ $t('console-actions') }}</UiCardTitle>
|
|
3
3
|
<UiButton
|
|
4
|
-
v-tooltip="toggleFullScreen === undefined ? $t('coming-soon') : undefined"
|
|
5
4
|
class="button"
|
|
6
|
-
:disabled="toggleFullScreen === undefined"
|
|
7
5
|
accent="info"
|
|
8
6
|
variant="tertiary"
|
|
9
7
|
size="medium"
|
|
@@ -13,9 +11,7 @@
|
|
|
13
11
|
{{ $t(isFullscreen ? 'exit-fullscreen' : 'fullscreen') }}
|
|
14
12
|
</UiButton>
|
|
15
13
|
<UiButton
|
|
16
|
-
v-tooltip="openInNewTab === undefined ? $t('coming-soon') : undefined"
|
|
17
14
|
class="button"
|
|
18
|
-
:disabled="openInNewTab === undefined"
|
|
19
15
|
accent="info"
|
|
20
16
|
variant="tertiary"
|
|
21
17
|
size="medium"
|
|
@@ -25,12 +21,10 @@
|
|
|
25
21
|
{{ $t('open-console-in-new-tab') }}
|
|
26
22
|
</UiButton>
|
|
27
23
|
<UiButton
|
|
28
|
-
v-tooltip="sendCtrlAltDel === undefined ? $t('coming-soon') : undefined"
|
|
29
24
|
class="button"
|
|
30
25
|
accent="info"
|
|
31
26
|
variant="tertiary"
|
|
32
27
|
size="medium"
|
|
33
|
-
:disabled="sendCtrlAltDel === undefined"
|
|
34
28
|
:left-icon="faKeyboard"
|
|
35
29
|
@click="sendCtrlAltDel"
|
|
36
30
|
>
|
|
@@ -41,21 +35,41 @@
|
|
|
41
35
|
<script lang="ts" setup>
|
|
42
36
|
import UiButton from '@core/components/ui/button/UiButton.vue'
|
|
43
37
|
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
|
44
|
-
import {
|
|
38
|
+
import { useUiStore } from '@core/stores/ui.store'
|
|
45
39
|
import {
|
|
46
40
|
faArrowUpRightFromSquare,
|
|
47
41
|
faDownLeftAndUpRightToCenter,
|
|
48
42
|
faKeyboard,
|
|
49
43
|
faUpRightAndDownLeftFromCenter,
|
|
50
44
|
} from '@fortawesome/free-solid-svg-icons'
|
|
45
|
+
import { useActiveElement, useMagicKeys, whenever } from '@vueuse/core'
|
|
46
|
+
import { logicAnd } from '@vueuse/math'
|
|
47
|
+
import { computed } from 'vue'
|
|
48
|
+
import { useRouter } from 'vue-router'
|
|
51
49
|
|
|
52
|
-
// temporary undefined for xo6
|
|
53
50
|
defineProps<{
|
|
54
|
-
|
|
55
|
-
toggleFullScreen?: () => void
|
|
56
|
-
sendCtrlAltDel?: () => void
|
|
57
|
-
isFullscreen?: boolean
|
|
51
|
+
sendCtrlAltDel: () => void
|
|
58
52
|
}>()
|
|
53
|
+
|
|
54
|
+
const router = useRouter()
|
|
55
|
+
const uiStore = useUiStore()
|
|
56
|
+
|
|
57
|
+
const isFullscreen = computed(() => !uiStore.hasUi)
|
|
58
|
+
|
|
59
|
+
const openInNewTab = () => {
|
|
60
|
+
const routeData = router.resolve({ query: { ui: '0' } })
|
|
61
|
+
window.open(routeData.href, '_blank')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const toggleFullScreen = () => {
|
|
65
|
+
uiStore.hasUi = !uiStore.hasUi
|
|
66
|
+
}
|
|
67
|
+
const { escape } = useMagicKeys()
|
|
68
|
+
const activeElement = useActiveElement()
|
|
69
|
+
const canClose = computed(
|
|
70
|
+
() => (activeElement.value == null || activeElement.value.tagName !== 'CANVAS') && !uiStore.hasUi
|
|
71
|
+
)
|
|
72
|
+
whenever(logicAnd(escape, canClose), toggleFullScreen)
|
|
59
73
|
</script>
|
|
60
74
|
|
|
61
75
|
<style lang="postcss" scoped>
|
|
@@ -1,51 +1,61 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="uiStore.isMobile ? 'mobile' : undefined" class="vts-remote-console">
|
|
3
|
-
<
|
|
3
|
+
<VtsLoadingHero :disabled="isReady" type="panel" />
|
|
4
|
+
<div ref="console-container" class="console" />
|
|
4
5
|
</div>
|
|
5
6
|
</template>
|
|
6
7
|
|
|
7
8
|
<script lang="ts" setup>
|
|
9
|
+
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
|
8
10
|
import { useUiStore } from '@core/stores/ui.store'
|
|
9
11
|
import VncClient from '@novnc/novnc/lib/rfb'
|
|
12
|
+
import { whenever } from '@vueuse/core'
|
|
10
13
|
import { promiseTimeout } from '@vueuse/shared'
|
|
11
14
|
import { fibonacci } from 'iterable-backoff'
|
|
12
|
-
import { onBeforeUnmount, ref, watchEffect } from 'vue'
|
|
15
|
+
import { onBeforeUnmount, ref, useTemplateRef, watchEffect } from 'vue'
|
|
13
16
|
|
|
14
17
|
const props = defineProps<{
|
|
15
18
|
url: URL
|
|
19
|
+
isConsoleAvailable: boolean
|
|
16
20
|
}>()
|
|
17
21
|
|
|
22
|
+
const uiStore = useUiStore()
|
|
23
|
+
|
|
18
24
|
const N_TOTAL_TRIES = 8
|
|
19
25
|
const FIBONACCI_MS_ARRAY: number[] = Array.from(fibonacci().toMs().take(N_TOTAL_TRIES))
|
|
20
26
|
|
|
21
|
-
const consoleContainer =
|
|
27
|
+
const consoleContainer = useTemplateRef<HTMLDivElement | null>('console-container')
|
|
28
|
+
const isReady = ref(false)
|
|
22
29
|
|
|
23
30
|
let vncClient: VncClient | undefined
|
|
24
31
|
let nConnectionAttempts = 0
|
|
25
32
|
|
|
26
33
|
function handleDisconnectionEvent() {
|
|
27
34
|
clearVncClient()
|
|
35
|
+
if (props.isConsoleAvailable) {
|
|
36
|
+
nConnectionAttempts++
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
if (nConnectionAttempts > N_TOTAL_TRIES) {
|
|
39
|
+
console.error('The number of reconnection attempts has been exceeded for:', props.url)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
console.error(
|
|
44
|
+
`Connection lost for the remote console: ${props.url}. New attempt in ${
|
|
45
|
+
FIBONACCI_MS_ARRAY[nConnectionAttempts - 1]
|
|
46
|
+
}ms`
|
|
47
|
+
)
|
|
48
|
+
createVncConnection()
|
|
34
49
|
}
|
|
35
|
-
|
|
36
|
-
console.error(
|
|
37
|
-
`Connection lost for the remote console: ${props.url}. New attempt in ${
|
|
38
|
-
FIBONACCI_MS_ARRAY[nConnectionAttempts - 1]
|
|
39
|
-
}ms`
|
|
40
|
-
)
|
|
41
|
-
createVncConnection()
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
function handleConnectionEvent() {
|
|
45
53
|
nConnectionAttempts = 0
|
|
54
|
+
isReady.value = true
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
function clearVncClient() {
|
|
58
|
+
isReady.value = false
|
|
49
59
|
if (vncClient === undefined) {
|
|
50
60
|
return
|
|
51
61
|
}
|
|
@@ -75,10 +85,34 @@ async function createVncConnection() {
|
|
|
75
85
|
|
|
76
86
|
vncClient.addEventListener('disconnect', handleDisconnectionEvent)
|
|
77
87
|
vncClient.addEventListener('connect', handleConnectionEvent)
|
|
88
|
+
const canvas = consoleContainer.value?.querySelector('canvas') as HTMLCanvasElement | null
|
|
89
|
+
if (canvas !== null) {
|
|
90
|
+
// Todo: See with Clémence to specify the desired focus behavior
|
|
91
|
+
canvas.setAttribute('tabindex', '0')
|
|
92
|
+
canvas.addEventListener('focus', () => {
|
|
93
|
+
canvas.classList.add('focused')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
canvas.addEventListener('blur', () => {
|
|
97
|
+
canvas.classList.remove('focused')
|
|
98
|
+
})
|
|
99
|
+
}
|
|
78
100
|
}
|
|
79
101
|
|
|
102
|
+
whenever(
|
|
103
|
+
() => uiStore.hasUi,
|
|
104
|
+
() => {
|
|
105
|
+
if (!vncClient) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
// the state is changed from false to true for xo-lite to trigger the change
|
|
109
|
+
vncClient.scaleViewport = false
|
|
110
|
+
vncClient.scaleViewport = true
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
80
114
|
watchEffect(() => {
|
|
81
|
-
if (consoleContainer.value === null) {
|
|
115
|
+
if (consoleContainer.value === null || !props.isConsoleAvailable) {
|
|
82
116
|
return
|
|
83
117
|
}
|
|
84
118
|
|
|
@@ -92,7 +126,9 @@ onBeforeUnmount(() => {
|
|
|
92
126
|
clearVncClient()
|
|
93
127
|
})
|
|
94
128
|
|
|
95
|
-
|
|
129
|
+
defineExpose({
|
|
130
|
+
sendCtrlAltDel: () => vncClient?.sendCtrlAltDel(),
|
|
131
|
+
})
|
|
96
132
|
</script>
|
|
97
133
|
|
|
98
134
|
<style lang="postcss" scoped>
|
|
@@ -113,6 +149,12 @@ const uiStore = useUiStore()
|
|
|
113
149
|
/* Required because the library adds "margin: auto" to the canvas which makes the canvas centered in space and not aligned to the rest of the layout */
|
|
114
150
|
:deep(canvas) {
|
|
115
151
|
margin: 0 auto !important;
|
|
152
|
+
cursor: default !important;
|
|
153
|
+
border: 6px solid transparent;
|
|
154
|
+
|
|
155
|
+
&.focused {
|
|
156
|
+
border: var(--color-success-txt-base) 6px solid;
|
|
157
|
+
}
|
|
116
158
|
}
|
|
117
159
|
}
|
|
118
160
|
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="table-container">
|
|
3
|
+
<VtsLoadingHero :disabled="isReady" type="table">
|
|
4
|
+
<VtsTable vertical-border>
|
|
5
|
+
<thead>
|
|
6
|
+
<slot name="thead" />
|
|
7
|
+
</thead>
|
|
8
|
+
<tbody>
|
|
9
|
+
<slot name="tbody" />
|
|
10
|
+
</tbody>
|
|
11
|
+
</VtsTable>
|
|
12
|
+
</VtsLoadingHero>
|
|
13
|
+
<VtsErrorNoDataHero v-if="isReady && hasError" type="table" />
|
|
14
|
+
<VtsStateHero v-if="isReady && noDataMessage" type="table" image="no-data" />
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
|
20
|
+
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
|
21
|
+
import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
|
|
22
|
+
import VtsTable from '@core/components/table/VtsTable.vue'
|
|
23
|
+
|
|
24
|
+
defineProps<{
|
|
25
|
+
isReady?: boolean
|
|
26
|
+
hasError?: boolean
|
|
27
|
+
noDataMessage?: string
|
|
28
|
+
}>()
|
|
29
|
+
|
|
30
|
+
defineSlots<{
|
|
31
|
+
thead(): any
|
|
32
|
+
tbody(): any
|
|
33
|
+
}>()
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<style lang="postcss" scoped>
|
|
37
|
+
.table-container {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
gap: 0.8rem;
|
|
41
|
+
|
|
42
|
+
:deep(tbody) tr {
|
|
43
|
+
&:hover {
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
background-color: var(--color-info-background-hover);
|
|
46
|
+
}
|
|
47
|
+
&:active {
|
|
48
|
+
background-color: var(--color-info-background-active);
|
|
49
|
+
}
|
|
50
|
+
&.selected {
|
|
51
|
+
background-color: var(--color-info-background-selected);
|
|
52
|
+
}
|
|
53
|
+
&:last-child {
|
|
54
|
+
border-bottom: 0.1rem solid var(--color-neutral-border);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
>
|
|
12
12
|
<slot />
|
|
13
13
|
</MenuTrigger>
|
|
14
|
-
<MenuList v-else :disabled="isDisabled"
|
|
14
|
+
<MenuList v-else :disabled="isDisabled">
|
|
15
15
|
<template #trigger="{ open, isOpen }">
|
|
16
16
|
<MenuTrigger :active="isOpen" :busy="isBusy" :disabled="isDisabled" :icon @click="open">
|
|
17
17
|
<slot />
|
|
@@ -27,28 +27,24 @@
|
|
|
27
27
|
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
|
28
28
|
import MenuList from '@core/components/menu/MenuList.vue'
|
|
29
29
|
import MenuTrigger from '@core/components/menu/MenuTrigger.vue'
|
|
30
|
-
import {
|
|
31
|
-
import { DisabledContext } from '@core/context'
|
|
30
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
32
31
|
import { IK_CLOSE_MENU, IK_MENU_HORIZONTAL } from '@core/utils/injection-keys.util'
|
|
33
32
|
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
|
|
34
33
|
import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
|
35
34
|
import { computed, inject, ref } from 'vue'
|
|
36
35
|
|
|
37
|
-
const props =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}>(),
|
|
44
|
-
{ disabled: undefined }
|
|
45
|
-
)
|
|
36
|
+
const props = defineProps<{
|
|
37
|
+
icon?: IconDefinition
|
|
38
|
+
onClick?: () => any
|
|
39
|
+
disabled?: boolean
|
|
40
|
+
busy?: boolean
|
|
41
|
+
}>()
|
|
46
42
|
|
|
47
43
|
const isParentHorizontal = inject(
|
|
48
44
|
IK_MENU_HORIZONTAL,
|
|
49
45
|
computed(() => false)
|
|
50
46
|
)
|
|
51
|
-
const isDisabled =
|
|
47
|
+
const isDisabled = useDisabled(() => props.disabled)
|
|
52
48
|
|
|
53
49
|
const submenuIcon = computed(() => (isParentHorizontal.value ? faAngleDown : faAngleRight))
|
|
54
50
|
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
<template>
|
|
3
3
|
<slot :is-open="isOpen" :open="open" name="trigger" />
|
|
4
4
|
<Teleport :disabled="!shouldTeleport" to="body">
|
|
5
|
-
<ul
|
|
5
|
+
<ul
|
|
6
|
+
v-if="!hasTrigger || isOpen"
|
|
7
|
+
ref="menu"
|
|
8
|
+
:class="{ horizontal, border: !noBorder }"
|
|
9
|
+
class="menu-list"
|
|
10
|
+
v-bind="$attrs"
|
|
11
|
+
>
|
|
6
12
|
<slot />
|
|
7
13
|
</ul>
|
|
8
14
|
</Teleport>
|
|
9
15
|
</template>
|
|
10
16
|
|
|
11
17
|
<script lang="ts" setup>
|
|
12
|
-
import {
|
|
13
|
-
import { DisabledContext } from '@core/context'
|
|
18
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
14
19
|
import { IK_CLOSE_MENU, IK_MENU_HORIZONTAL, IK_MENU_TELEPORTED } from '@core/utils/injection-keys.util'
|
|
15
20
|
import { onClickOutside, unrefElement, whenever } from '@vueuse/core'
|
|
16
21
|
import placementJs, { type Options } from 'placement.js'
|
|
@@ -20,15 +25,12 @@ defineOptions({
|
|
|
20
25
|
inheritAttrs: false,
|
|
21
26
|
})
|
|
22
27
|
|
|
23
|
-
const props =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}>(),
|
|
30
|
-
{ disabled: undefined }
|
|
31
|
-
)
|
|
28
|
+
const props = defineProps<{
|
|
29
|
+
horizontal?: boolean
|
|
30
|
+
noBorder?: boolean
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
placement?: Options['placement']
|
|
33
|
+
}>()
|
|
32
34
|
|
|
33
35
|
const slots = useSlots()
|
|
34
36
|
const isOpen = ref(false)
|
|
@@ -42,7 +44,7 @@ provide(
|
|
|
42
44
|
computed(() => props.horizontal ?? false)
|
|
43
45
|
)
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
useDisabled(() => props.disabled)
|
|
46
48
|
|
|
47
49
|
let clearClickOutsideEvent: (() => void) | undefined
|
|
48
50
|
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script lang="ts" setup>
|
|
9
|
-
import {
|
|
10
|
-
import { DisabledContext } from '@core/context'
|
|
9
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
11
10
|
import { useUiStore } from '@core/stores/ui.store'
|
|
12
11
|
import { storeToRefs } from 'pinia'
|
|
13
12
|
import { computed } from 'vue'
|
|
@@ -18,12 +17,12 @@ const props = withDefaults(
|
|
|
18
17
|
active?: boolean
|
|
19
18
|
tag?: string
|
|
20
19
|
}>(),
|
|
21
|
-
{ tag: 'span'
|
|
20
|
+
{ tag: 'span' }
|
|
22
21
|
)
|
|
23
22
|
|
|
24
23
|
const { isMobile } = storeToRefs(useUiStore())
|
|
25
24
|
|
|
26
|
-
const isDisabled =
|
|
25
|
+
const isDisabled = useDisabled(() => props.disabled)
|
|
27
26
|
|
|
28
27
|
const classNames = computed(() => {
|
|
29
28
|
return [
|
|
@@ -6,17 +6,13 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script lang="ts" setup>
|
|
9
|
-
import {
|
|
10
|
-
import { DisabledContext } from '@core/context'
|
|
9
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
11
10
|
|
|
12
|
-
const props =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}>(),
|
|
16
|
-
{ disabled: undefined }
|
|
17
|
-
)
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
}>()
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
useDisabled(() => props.disabled)
|
|
20
16
|
</script>
|
|
21
17
|
|
|
22
18
|
<style lang="postcss" scoped>
|
|
@@ -7,15 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
<script lang="ts" setup>
|
|
9
9
|
import UiUserLogo from '@core/components/ui/user-logo/UiUserLogo.vue'
|
|
10
|
-
import {
|
|
11
|
-
import { DisabledContext } from '@core/context'
|
|
10
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
12
11
|
|
|
13
12
|
defineProps<{
|
|
14
13
|
size: 'small' | 'medium'
|
|
15
14
|
selected?: boolean
|
|
16
15
|
}>()
|
|
17
16
|
|
|
18
|
-
const isDisabled =
|
|
17
|
+
const isDisabled = useDisabled()
|
|
19
18
|
</script>
|
|
20
19
|
|
|
21
20
|
<style lang="postcss" scoped>
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<script lang="ts" setup>
|
|
11
11
|
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
|
12
|
-
import {
|
|
13
|
-
import { DisabledContext } from '@core/context'
|
|
12
|
+
import { useDisabled } from '@core/composables/disabled.composable'
|
|
14
13
|
import { toVariants } from '@core/utils/to-variants.util'
|
|
15
14
|
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
|
|
16
15
|
import { faLock } from '@fortawesome/free-solid-svg-icons'
|
|
@@ -20,26 +19,21 @@ type ButtonVariant = 'primary' | 'secondary' | 'tertiary'
|
|
|
20
19
|
type ButtonAccent = 'info' | 'success' | 'warning' | 'danger'
|
|
21
20
|
type ButtonSize = 'small' | 'medium' | 'large'
|
|
22
21
|
|
|
23
|
-
const props =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}>(),
|
|
33
|
-
{
|
|
34
|
-
disabled: undefined,
|
|
35
|
-
}
|
|
36
|
-
)
|
|
22
|
+
const props = defineProps<{
|
|
23
|
+
variant: ButtonVariant
|
|
24
|
+
accent: ButtonAccent
|
|
25
|
+
size: ButtonSize
|
|
26
|
+
busy?: boolean
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
lockIcon?: boolean
|
|
29
|
+
leftIcon?: IconDefinition
|
|
30
|
+
}>()
|
|
37
31
|
|
|
38
32
|
defineSlots<{
|
|
39
33
|
default(): any
|
|
40
34
|
}>()
|
|
41
35
|
|
|
42
|
-
const isDisabled =
|
|
36
|
+
const isDisabled = useDisabled(() => props.disabled)
|
|
43
37
|
|
|
44
38
|
const fontClasses = {
|
|
45
39
|
small: 'typo p3-medium',
|