pimelon-ui 0.1.31 → 0.1.45
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/package.json +4 -8
- package/src/components/Autocomplete.vue +1 -1
- package/src/components/FormControl.story.vue +10 -9
- package/src/components/ListFilter/ListFilter.vue +1 -1
- package/src/components/ListFilter/SearchComplete.vue +1 -1
- package/src/components/ListView/ListEmptyState.vue +24 -0
- package/src/components/ListView/ListGroupHeader.vue +44 -0
- package/src/components/ListView/ListGroupRows.vue +17 -0
- package/src/components/ListView/ListGroups.vue +22 -0
- package/src/components/ListView/ListHeaderItem.vue +2 -2
- package/src/components/ListView/ListRow.vue +26 -2
- package/src/components/ListView/ListRowItem.vue +21 -4
- package/src/components/ListView/ListView.vue +38 -8
- package/src/components/ListView.story.md +26 -1
- package/src/components/ListView.story.vue +235 -9
- package/src/components/Popover.vue +29 -7
- package/src/components/Progress.vue +1 -1
- package/src/components/Select.vue +37 -14
- package/src/icons/DownSolid.vue +8 -0
- package/src/index.js +4 -0
- package/src/resources/listResource.js +3 -3
- package/src/resources/plugin.js +2 -4
- package/src/utils/melonRequest.js +1 -1
- package/vite.js +14 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pimelon-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.45",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
"prettier": "npx prettier -w ./src",
|
|
9
9
|
"prepare": "husky install",
|
|
10
10
|
"bump-and-release": "git pull --rebase origin main && yarn version --patch && git push && git push --tags",
|
|
11
|
-
"docs:dev": "vitepress dev docs",
|
|
12
|
-
"docs:build": "vitepress build docs",
|
|
13
|
-
"docs:serve": "vitepress serve docs",
|
|
14
11
|
"dev": "vite",
|
|
15
12
|
"build": "vite build",
|
|
16
13
|
"preview": "vite preview",
|
|
@@ -62,12 +59,12 @@
|
|
|
62
59
|
"vue-router": "^4.1.6"
|
|
63
60
|
},
|
|
64
61
|
"devDependencies": {
|
|
65
|
-
"@histoire/plugin-vue": "^0.
|
|
62
|
+
"@histoire/plugin-vue": "^0.17.14",
|
|
66
63
|
"@vitejs/plugin-vue": "^4.0.0",
|
|
67
64
|
"autoprefixer": "^10.4.13",
|
|
68
65
|
"cross-fetch": "^3.1.5",
|
|
69
|
-
"histoire": "^0.
|
|
70
|
-
"husky": "^9.
|
|
66
|
+
"histoire": "^0.17.14",
|
|
67
|
+
"husky": "^9.1.6",
|
|
71
68
|
"lint-staged": ">=10",
|
|
72
69
|
"postcss": "^8.4.21",
|
|
73
70
|
"prettier": "2.7.1",
|
|
@@ -75,7 +72,6 @@
|
|
|
75
72
|
"tailwindcss": "^3.2.7",
|
|
76
73
|
"typescript": "^5.0.2",
|
|
77
74
|
"vite": "^4.1.0",
|
|
78
|
-
"vitepress": "^1.0.0-alpha.29",
|
|
79
75
|
"vue": "^3.2.45",
|
|
80
76
|
"vue-router": "^4.1.6"
|
|
81
77
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<span class="truncate text-base leading-5" v-if="selectedValue">
|
|
20
20
|
{{ displayValue(selectedValue) }}
|
|
21
21
|
</span>
|
|
22
|
-
<span class="text-base leading-5 text-gray-
|
|
22
|
+
<span class="text-base leading-5 text-gray-500" v-else>
|
|
23
23
|
{{ placeholder || '' }}
|
|
24
24
|
</span>
|
|
25
25
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { reactive } from 'vue'
|
|
2
|
+
import { reactive, ref } from 'vue'
|
|
3
3
|
import FormControl from './FormControl.vue'
|
|
4
4
|
import FeatherIcon from './FeatherIcon.vue'
|
|
5
5
|
import Avatar from './Avatar.vue'
|
|
@@ -10,8 +10,11 @@ const state = reactive({
|
|
|
10
10
|
placeholder: 'Placeholder',
|
|
11
11
|
disabled: false,
|
|
12
12
|
label: 'Label',
|
|
13
|
-
modelValue: '',
|
|
14
13
|
})
|
|
14
|
+
const inputValue = ref('')
|
|
15
|
+
const selectValue = ref(null)
|
|
16
|
+
const autocompleteValue = ref(null)
|
|
17
|
+
const checkboxValue = ref(false)
|
|
15
18
|
|
|
16
19
|
const inputTypes = [
|
|
17
20
|
'text',
|
|
@@ -34,11 +37,7 @@ const variants = ['subtle', 'outline']
|
|
|
34
37
|
:title="inputType"
|
|
35
38
|
>
|
|
36
39
|
<div class="p-2">
|
|
37
|
-
<FormControl
|
|
38
|
-
:type="inputType"
|
|
39
|
-
v-bind="state"
|
|
40
|
-
v-model="state.modelValue"
|
|
41
|
-
/>
|
|
40
|
+
<FormControl :type="inputType" v-bind="state" v-model="inputValue" />
|
|
42
41
|
</div>
|
|
43
42
|
</Variant>
|
|
44
43
|
<Variant title="select">
|
|
@@ -51,10 +50,11 @@ const variants = ['subtle', 'outline']
|
|
|
51
50
|
{ label: 'Three', value: '3' },
|
|
52
51
|
]"
|
|
53
52
|
v-bind="state"
|
|
53
|
+
v-model="selectValue"
|
|
54
54
|
/>
|
|
55
55
|
</div>
|
|
56
56
|
</Variant>
|
|
57
|
-
<Variant title="
|
|
57
|
+
<Variant title="autocomplete">
|
|
58
58
|
<div class="p-2">
|
|
59
59
|
<FormControl
|
|
60
60
|
type="autocomplete"
|
|
@@ -64,12 +64,13 @@ const variants = ['subtle', 'outline']
|
|
|
64
64
|
{ label: 'Three', value: '3' },
|
|
65
65
|
]"
|
|
66
66
|
v-bind="state"
|
|
67
|
+
v-model="autocompleteValue"
|
|
67
68
|
/>
|
|
68
69
|
</div>
|
|
69
70
|
</Variant>
|
|
70
71
|
<Variant title="checkbox">
|
|
71
72
|
<div class="p-2">
|
|
72
|
-
<FormControl type="checkbox" v-bind="state" />
|
|
73
|
+
<FormControl type="checkbox" v-bind="state" v-model="checkboxValue" />
|
|
73
74
|
</div>
|
|
74
75
|
</Variant>
|
|
75
76
|
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
</template>
|
|
115
115
|
|
|
116
116
|
<script setup>
|
|
117
|
-
import { Autocomplete, FeatherIcon, FormControl } from '
|
|
117
|
+
import { Autocomplete, FeatherIcon, FormControl } from '../../index'
|
|
118
118
|
import { computed, h } from 'vue'
|
|
119
119
|
import FilterIcon from './FilterIcon.vue'
|
|
120
120
|
import NestedPopover from './NestedPopover.vue'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex h-full w-full flex-col items-center justify-center text-base"
|
|
4
|
+
>
|
|
5
|
+
<slot>
|
|
6
|
+
<div class="text-xl font-medium">{{ list.options.emptyState.title }}</div>
|
|
7
|
+
<div class="mt-1 text-base text-gray-600">
|
|
8
|
+
{{ list.options.emptyState.description }}
|
|
9
|
+
</div>
|
|
10
|
+
<Button
|
|
11
|
+
v-if="list.options.emptyState.button"
|
|
12
|
+
v-bind="list.options.emptyState.button"
|
|
13
|
+
class="mt-4"
|
|
14
|
+
></Button>
|
|
15
|
+
</slot>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
import { inject } from 'vue'
|
|
21
|
+
import Button from '../Button.vue'
|
|
22
|
+
|
|
23
|
+
const list = inject('list')
|
|
24
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center">
|
|
3
|
+
<button
|
|
4
|
+
@click="toggleGroup"
|
|
5
|
+
class="ml-[3px] mr-[11px] rounded p-1 hover:bg-gray-100"
|
|
6
|
+
>
|
|
7
|
+
<DownSolid
|
|
8
|
+
class="h-4 w-4 text-gray-900 transition-transform duration-200"
|
|
9
|
+
:class="[group.collapsed ? '-rotate-90' : '']"
|
|
10
|
+
/>
|
|
11
|
+
</button>
|
|
12
|
+
<div class="w-full py-1.5 pr-2">
|
|
13
|
+
<component
|
|
14
|
+
v-if="list.slots['group-header']"
|
|
15
|
+
:is="list.slots['group-header']"
|
|
16
|
+
v-bind="{ group }"
|
|
17
|
+
/>
|
|
18
|
+
<span v-else class="text-base font-medium leading-6">
|
|
19
|
+
{{ group.group }}
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="mx-2 h-px border-t border-gray-200"></div>
|
|
24
|
+
</template>
|
|
25
|
+
<script setup>
|
|
26
|
+
import { inject } from 'vue'
|
|
27
|
+
import DownSolid from '../../icons/DownSolid.vue'
|
|
28
|
+
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
group: {
|
|
31
|
+
type: Object,
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const list = inject('list')
|
|
37
|
+
|
|
38
|
+
function toggleGroup() {
|
|
39
|
+
if (props.group.collapsed == null) {
|
|
40
|
+
props.group.collapsed = false
|
|
41
|
+
}
|
|
42
|
+
props.group.collapsed = !props.group.collapsed
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mb-5 mt-2" v-if="!group.collapsed">
|
|
3
|
+
<ListRow v-for="row in group.rows" :key="row[list.rowKey]" :row="row" />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
<script setup>
|
|
7
|
+
import ListRow from './ListRow.vue'
|
|
8
|
+
import { inject } from 'vue'
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
group: {
|
|
12
|
+
type: Object,
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
const list = inject('list')
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-full overflow-y-auto">
|
|
3
|
+
<div v-for="group in list.rows" :key="group.group">
|
|
4
|
+
<ListGroupHeader :group="group">
|
|
5
|
+
<slot
|
|
6
|
+
name="group-header"
|
|
7
|
+
v-if="$slots['group-header']"
|
|
8
|
+
v-bind="{ group }"
|
|
9
|
+
/>
|
|
10
|
+
</ListGroupHeader>
|
|
11
|
+
<ListGroupRows :group="group" />
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
import ListGroupHeader from './ListGroupHeader.vue'
|
|
18
|
+
import ListGroupRows from './ListGroupRows.vue'
|
|
19
|
+
import { inject } from 'vue'
|
|
20
|
+
|
|
21
|
+
const list = inject('list')
|
|
22
|
+
</script>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
ref="columnRef"
|
|
4
|
-
class="group flex items-center
|
|
5
|
-
:class="alignmentMap[item.align]"
|
|
4
|
+
class="group flex items-center"
|
|
5
|
+
:class="item.align ? alignmentMap[item.align] : 'justify-between'"
|
|
6
6
|
>
|
|
7
7
|
<div
|
|
8
8
|
class="flex items-center space-x-2 truncate text-sm text-gray-600"
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
class="[all:unset] hover:[all:unset]"
|
|
15
15
|
>
|
|
16
16
|
<div
|
|
17
|
-
class="grid items-center space-x-4 rounded px-2
|
|
17
|
+
class="grid items-center space-x-4 rounded px-2"
|
|
18
18
|
:class="
|
|
19
19
|
list.selections.has(row[list.rowKey])
|
|
20
20
|
? 'bg-gray-100 hover:bg-gray-200'
|
|
21
21
|
: 'hover:bg-gray-50'
|
|
22
22
|
"
|
|
23
23
|
:style="{
|
|
24
|
+
height: rowHeight,
|
|
24
25
|
gridTemplateColumns: getGridTemplateColumns(
|
|
25
26
|
list.columns,
|
|
26
27
|
list.options.selectable
|
|
@@ -42,7 +43,23 @@
|
|
|
42
43
|
]"
|
|
43
44
|
>
|
|
44
45
|
<slot v-bind="{ idx: i, column, item: row[column.key] }">
|
|
45
|
-
<
|
|
46
|
+
<component
|
|
47
|
+
v-if="list.slots.cell"
|
|
48
|
+
:is="list.slots.cell"
|
|
49
|
+
v-bind="{
|
|
50
|
+
column,
|
|
51
|
+
row,
|
|
52
|
+
item: row[column.key],
|
|
53
|
+
align: column.align,
|
|
54
|
+
}"
|
|
55
|
+
/>
|
|
56
|
+
<ListRowItem
|
|
57
|
+
v-else
|
|
58
|
+
:column="column"
|
|
59
|
+
:row="row"
|
|
60
|
+
:item="row[column.key]"
|
|
61
|
+
:align="column.align"
|
|
62
|
+
/>
|
|
46
63
|
</slot>
|
|
47
64
|
</div>
|
|
48
65
|
</div>
|
|
@@ -73,4 +90,11 @@ const isLastRow = computed(() => {
|
|
|
73
90
|
props.row[list.value.rowKey]
|
|
74
91
|
)
|
|
75
92
|
})
|
|
93
|
+
|
|
94
|
+
const rowHeight = computed(() => {
|
|
95
|
+
if (typeof list.value.options.rowHeight === 'number') {
|
|
96
|
+
return `${list.value.options.rowHeight}px`
|
|
97
|
+
}
|
|
98
|
+
return list.value.options.rowHeight
|
|
99
|
+
})
|
|
76
100
|
</script>
|
|
@@ -5,21 +5,38 @@
|
|
|
5
5
|
class="flex items-center space-x-2"
|
|
6
6
|
:class="alignmentMap[align]"
|
|
7
7
|
>
|
|
8
|
-
<slot name="prefix"
|
|
8
|
+
<slot name="prefix">
|
|
9
|
+
<component
|
|
10
|
+
v-if="column.prefix"
|
|
11
|
+
:is="
|
|
12
|
+
typeof column.prefix === 'function'
|
|
13
|
+
? column.prefix({ row })
|
|
14
|
+
: column.prefix
|
|
15
|
+
"
|
|
16
|
+
/>
|
|
17
|
+
</slot>
|
|
9
18
|
<slot v-bind="{ label }">
|
|
10
19
|
<div class="truncate text-base">
|
|
11
|
-
{{ label }}
|
|
20
|
+
{{ column?.getLabel ? column.getLabel({ row }) : label }}
|
|
12
21
|
</div>
|
|
13
22
|
</slot>
|
|
14
23
|
<slot name="suffix" />
|
|
15
24
|
</component>
|
|
16
25
|
</template>
|
|
17
26
|
<script setup>
|
|
18
|
-
import { alignmentMap } from './utils'
|
|
19
|
-
import Tooltip from '../Tooltip.vue'
|
|
20
27
|
import { computed, inject } from 'vue'
|
|
28
|
+
import Tooltip from '../Tooltip.vue'
|
|
29
|
+
import { alignmentMap } from './utils'
|
|
21
30
|
|
|
22
31
|
const props = defineProps({
|
|
32
|
+
column: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: {},
|
|
35
|
+
},
|
|
36
|
+
row: {
|
|
37
|
+
type: Object,
|
|
38
|
+
default: {},
|
|
39
|
+
},
|
|
23
40
|
item: {
|
|
24
41
|
type: [String, Number, Object],
|
|
25
42
|
default: '',
|
|
@@ -4,19 +4,25 @@
|
|
|
4
4
|
class="flex w-max min-w-full flex-col overflow-y-hidden"
|
|
5
5
|
:class="$attrs.class"
|
|
6
6
|
>
|
|
7
|
-
<slot>
|
|
7
|
+
<slot v-bind="{ showGroupedRows, selectable }">
|
|
8
8
|
<ListHeader />
|
|
9
|
-
<
|
|
10
|
-
|
|
9
|
+
<template v-if="props.rows.length">
|
|
10
|
+
<ListGroups v-if="showGroupedRows" />
|
|
11
|
+
<ListRows v-else />
|
|
12
|
+
</template>
|
|
13
|
+
<ListEmptyState v-else />
|
|
14
|
+
<ListSelectBanner v-if="selectable" />
|
|
11
15
|
</slot>
|
|
12
16
|
</div>
|
|
13
17
|
</div>
|
|
14
18
|
</template>
|
|
15
19
|
<script setup>
|
|
20
|
+
import ListEmptyState from './ListEmptyState.vue'
|
|
16
21
|
import ListHeader from './ListHeader.vue'
|
|
17
22
|
import ListRows from './ListRows.vue'
|
|
23
|
+
import ListGroups from './ListGroups.vue'
|
|
18
24
|
import ListSelectBanner from './ListSelectBanner.vue'
|
|
19
|
-
import { reactive, computed, provide, watch } from 'vue'
|
|
25
|
+
import { reactive, computed, provide, watch, useSlots } from 'vue'
|
|
20
26
|
|
|
21
27
|
defineOptions({
|
|
22
28
|
inheritAttrs: false,
|
|
@@ -37,16 +43,23 @@ const props = defineProps({
|
|
|
37
43
|
},
|
|
38
44
|
options: {
|
|
39
45
|
type: Object,
|
|
40
|
-
default: {
|
|
46
|
+
default: () => ({
|
|
41
47
|
getRowRoute: null,
|
|
42
48
|
onRowClick: null,
|
|
43
49
|
showTooltip: true,
|
|
44
50
|
selectable: true,
|
|
45
|
-
resizeColumn:
|
|
46
|
-
|
|
51
|
+
resizeColumn: false,
|
|
52
|
+
rowHeight: 40,
|
|
53
|
+
emptyState: {
|
|
54
|
+
title: 'No Data',
|
|
55
|
+
description: 'No data available',
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
47
58
|
},
|
|
48
59
|
})
|
|
49
60
|
|
|
61
|
+
const slots = useSlots()
|
|
62
|
+
|
|
50
63
|
let selections = reactive(new Set())
|
|
51
64
|
|
|
52
65
|
const emit = defineEmits(['update:selections'])
|
|
@@ -60,12 +73,18 @@ let _options = computed(() => {
|
|
|
60
73
|
return value === undefined ? true : value
|
|
61
74
|
}
|
|
62
75
|
|
|
76
|
+
function defaultFalse(value) {
|
|
77
|
+
return value === undefined ? false : value
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
return {
|
|
64
81
|
getRowRoute: props.options.getRowRoute || null,
|
|
65
82
|
onRowClick: props.options.onRowClick || null,
|
|
66
83
|
showTooltip: defaultTrue(props.options.showTooltip),
|
|
67
84
|
selectable: defaultTrue(props.options.selectable),
|
|
68
|
-
resizeColumn:
|
|
85
|
+
resizeColumn: defaultFalse(props.options.resizeColumn),
|
|
86
|
+
rowHeight: props.options.rowHeight || 40,
|
|
87
|
+
emptyState: props.options.emptyState,
|
|
69
88
|
}
|
|
70
89
|
})
|
|
71
90
|
|
|
@@ -74,6 +93,16 @@ const allRowsSelected = computed(() => {
|
|
|
74
93
|
return selections.size === props.rows.length
|
|
75
94
|
})
|
|
76
95
|
|
|
96
|
+
const selectable = computed(() => {
|
|
97
|
+
return _options.value.selectable
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
let showGroupedRows = computed(() => {
|
|
101
|
+
return props.rows.every(
|
|
102
|
+
(row) => row.group && row.rows && Array.isArray(row.rows)
|
|
103
|
+
)
|
|
104
|
+
})
|
|
105
|
+
|
|
77
106
|
function toggleRow(row) {
|
|
78
107
|
if (!selections.delete(row)) {
|
|
79
108
|
selections.add(row)
|
|
@@ -97,6 +126,7 @@ provide(
|
|
|
97
126
|
options: _options.value,
|
|
98
127
|
selections: selections,
|
|
99
128
|
allRowsSelected: allRowsSelected.value,
|
|
129
|
+
slots: slots,
|
|
100
130
|
toggleRow,
|
|
101
131
|
toggleAllRows,
|
|
102
132
|
}))
|
|
@@ -62,6 +62,31 @@ required to be passed in the `row` object.
|
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
### Grouped Rows
|
|
66
|
+
|
|
67
|
+
To render grouped rows, you must provide `rows` in the following format:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
group: 'Group Title 1',
|
|
73
|
+
collapsed: false,
|
|
74
|
+
rows: [
|
|
75
|
+
{id: 1, key1: value1, key2: value2, ...},
|
|
76
|
+
{id: 2, key1: value1, key2: value2, ...},
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
group: 'Group Title 2',
|
|
81
|
+
collapsed: false,
|
|
82
|
+
rows: [
|
|
83
|
+
{id: 3, key1: value1, key2: value2, ...},
|
|
84
|
+
{id: 4, key1: value1, key2: value2, ...},
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
]
|
|
88
|
+
```
|
|
89
|
+
|
|
65
90
|
### Options
|
|
66
91
|
|
|
67
92
|
1. If you want to route using router-link just add a `getRowRoute` function
|
|
@@ -78,7 +103,7 @@ required to be passed in the `row` object.
|
|
|
78
103
|
4. showTooltip (Boolean) - if true, tooltip will be shown on hover of row -
|
|
79
104
|
default is true
|
|
80
105
|
5. resizeColumn (Boolean) - if true, column can be resized by dragging the
|
|
81
|
-
resizer on the right side of the column header - default is
|
|
106
|
+
resizer on the right side of the column header - default is false
|
|
82
107
|
|
|
83
108
|
---
|
|
84
109
|
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
2
|
+
import { reactive, h, ref } from 'vue'
|
|
3
|
+
import Avatar from './Avatar.vue'
|
|
4
|
+
import Badge from './Badge.vue'
|
|
5
|
+
import Button from './Button.vue'
|
|
6
|
+
import FeatherIcon from './FeatherIcon.vue'
|
|
3
7
|
import ListHeader from './ListView/ListHeader.vue'
|
|
4
8
|
import ListHeaderItem from './ListView/ListHeaderItem.vue'
|
|
5
|
-
import ListRows from './ListView/ListRows.vue'
|
|
6
9
|
import ListRow from './ListView/ListRow.vue'
|
|
7
10
|
import ListRowItem from './ListView/ListRowItem.vue'
|
|
11
|
+
import ListRows from './ListView/ListRows.vue'
|
|
12
|
+
import ListGroups from './ListView/ListGroups.vue'
|
|
8
13
|
import ListSelectBanner from './ListView/ListSelectBanner.vue'
|
|
9
|
-
import
|
|
10
|
-
import Badge from './Badge.vue'
|
|
11
|
-
import Button from './Button.vue'
|
|
12
|
-
import Avatar from './Avatar.vue'
|
|
13
|
-
import { reactive } from 'vue'
|
|
14
|
+
import ListView from './ListView/ListView.vue'
|
|
14
15
|
|
|
15
16
|
const state = reactive({
|
|
16
17
|
selectable: true,
|
|
17
18
|
showTooltip: true,
|
|
18
19
|
resizeColumn: true,
|
|
20
|
+
emptyState: {
|
|
21
|
+
title: 'No records found',
|
|
22
|
+
description: 'Create a new record to get started',
|
|
23
|
+
button: {
|
|
24
|
+
label: 'New Record',
|
|
25
|
+
variant: 'solid',
|
|
26
|
+
onClick: () => console.log('New Record'),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
19
29
|
})
|
|
20
30
|
|
|
21
31
|
const simple_columns = reactive([
|
|
@@ -23,6 +33,14 @@ const simple_columns = reactive([
|
|
|
23
33
|
label: 'Name',
|
|
24
34
|
key: 'name',
|
|
25
35
|
width: 3,
|
|
36
|
+
getLabel: ({ row }) => row.name,
|
|
37
|
+
prefix: ({ row }) => {
|
|
38
|
+
return h(Avatar, {
|
|
39
|
+
shape: 'circle',
|
|
40
|
+
image: row.user_image,
|
|
41
|
+
size: 'sm',
|
|
42
|
+
})
|
|
43
|
+
},
|
|
26
44
|
},
|
|
27
45
|
{
|
|
28
46
|
label: 'Email',
|
|
@@ -46,6 +64,7 @@ const simple_rows = [
|
|
|
46
64
|
email: 'john@doe.com',
|
|
47
65
|
status: 'Active',
|
|
48
66
|
role: 'Developer',
|
|
67
|
+
user_image: 'https://avatars.githubusercontent.com/u/499550',
|
|
49
68
|
},
|
|
50
69
|
{
|
|
51
70
|
id: 2,
|
|
@@ -53,9 +72,148 @@ const simple_rows = [
|
|
|
53
72
|
email: 'jane@doe.com',
|
|
54
73
|
status: 'Inactive',
|
|
55
74
|
role: 'HR',
|
|
75
|
+
user_image: 'https://avatars.githubusercontent.com/u/499120',
|
|
56
76
|
},
|
|
57
77
|
]
|
|
58
78
|
|
|
79
|
+
const group_columns = reactive([
|
|
80
|
+
{
|
|
81
|
+
label: 'Name',
|
|
82
|
+
key: 'name',
|
|
83
|
+
width: 3,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: 'Email',
|
|
87
|
+
key: 'email',
|
|
88
|
+
width: '200px',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: 'Role',
|
|
92
|
+
key: 'role',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: 'Status',
|
|
96
|
+
key: 'status',
|
|
97
|
+
},
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
const grouped_rows = ref([
|
|
101
|
+
{
|
|
102
|
+
group: 'Developer',
|
|
103
|
+
collapsed: false,
|
|
104
|
+
rows: [
|
|
105
|
+
{
|
|
106
|
+
id: 2,
|
|
107
|
+
name: 'Gary Fox',
|
|
108
|
+
email: 'gary@fox.com',
|
|
109
|
+
status: 'Inactive',
|
|
110
|
+
role: 'Developer',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 6,
|
|
114
|
+
name: 'Emily Davis',
|
|
115
|
+
email: 'emily@davis.com',
|
|
116
|
+
status: 'Active',
|
|
117
|
+
role: 'Developer',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 9,
|
|
121
|
+
name: 'David Lee',
|
|
122
|
+
email: 'david@lee.com',
|
|
123
|
+
status: 'Inactive',
|
|
124
|
+
role: 'Developer',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
group: 'Manager',
|
|
130
|
+
collapsed: false,
|
|
131
|
+
rows: [
|
|
132
|
+
{
|
|
133
|
+
id: 3,
|
|
134
|
+
name: 'John Doe',
|
|
135
|
+
email: 'john@doe.com',
|
|
136
|
+
status: 'Active',
|
|
137
|
+
role: 'Manager',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 8,
|
|
141
|
+
name: 'Sarah Wilson',
|
|
142
|
+
email: 'sarah@wilson.com',
|
|
143
|
+
status: 'Active',
|
|
144
|
+
role: 'Manager',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
group: 'Designer',
|
|
150
|
+
collapsed: false,
|
|
151
|
+
rows: [
|
|
152
|
+
{
|
|
153
|
+
id: 4,
|
|
154
|
+
name: 'Alice Smith',
|
|
155
|
+
email: 'alice@smith.com',
|
|
156
|
+
status: 'Active',
|
|
157
|
+
role: 'Designer',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 10,
|
|
161
|
+
name: 'Olivia Taylor',
|
|
162
|
+
email: 'olivia@taylor.com',
|
|
163
|
+
status: 'Active',
|
|
164
|
+
role: 'Designer',
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
group: 'HR',
|
|
170
|
+
collapsed: false,
|
|
171
|
+
rows: [
|
|
172
|
+
{
|
|
173
|
+
id: 1,
|
|
174
|
+
name: 'Jane Mary',
|
|
175
|
+
email: 'jane@doe.com',
|
|
176
|
+
status: 'Inactive',
|
|
177
|
+
role: 'HR',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 7,
|
|
181
|
+
name: 'Michael Brown',
|
|
182
|
+
email: 'michael@brown.com',
|
|
183
|
+
status: 'Inactive',
|
|
184
|
+
role: 'HR',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 12,
|
|
188
|
+
name: 'Sophia Martinez',
|
|
189
|
+
email: 'sophia@martinez.com',
|
|
190
|
+
status: 'Active',
|
|
191
|
+
role: 'HR',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
group: 'Tester',
|
|
197
|
+
collapsed: false,
|
|
198
|
+
rows: [
|
|
199
|
+
{
|
|
200
|
+
id: 5,
|
|
201
|
+
name: 'Bob Johnson',
|
|
202
|
+
email: 'bob@johnson.com',
|
|
203
|
+
status: 'Inactive',
|
|
204
|
+
role: 'Tester',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 11,
|
|
208
|
+
name: 'James Anderson',
|
|
209
|
+
email: 'james@anderson.com',
|
|
210
|
+
status: 'Inactive',
|
|
211
|
+
role: 'Tester',
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
])
|
|
216
|
+
|
|
59
217
|
const custom_columns = reactive([
|
|
60
218
|
{
|
|
61
219
|
label: 'Name',
|
|
@@ -121,7 +279,7 @@ const custom_rows = [
|
|
|
121
279
|
<Story :layout="{ type: 'grid', width: '95%' }">
|
|
122
280
|
<Variant title="Simple List">
|
|
123
281
|
<ListView
|
|
124
|
-
class="h-[
|
|
282
|
+
class="h-[150px]"
|
|
125
283
|
:columns="simple_columns"
|
|
126
284
|
:rows="simple_rows"
|
|
127
285
|
:options="{
|
|
@@ -135,7 +293,7 @@ const custom_rows = [
|
|
|
135
293
|
</Variant>
|
|
136
294
|
<Variant title="Custom List">
|
|
137
295
|
<ListView
|
|
138
|
-
class="h-[
|
|
296
|
+
class="h-[150px]"
|
|
139
297
|
:columns="custom_columns"
|
|
140
298
|
:rows="custom_rows"
|
|
141
299
|
:options="{
|
|
@@ -202,11 +360,79 @@ const custom_rows = [
|
|
|
202
360
|
</ListSelectBanner>
|
|
203
361
|
</ListView>
|
|
204
362
|
</Variant>
|
|
363
|
+
<Variant title="Grouped Rows">
|
|
364
|
+
<ListView
|
|
365
|
+
class="h-[250px]"
|
|
366
|
+
:columns="group_columns"
|
|
367
|
+
:rows="grouped_rows"
|
|
368
|
+
:options="{
|
|
369
|
+
getRowRoute: (row) => ({ name: 'User', params: { userId: row.id } }),
|
|
370
|
+
selectable: state.selectable,
|
|
371
|
+
showTooltip: state.showTooltip,
|
|
372
|
+
resizeColumn: state.resizeColumn,
|
|
373
|
+
}"
|
|
374
|
+
row-key="id"
|
|
375
|
+
>
|
|
376
|
+
<template #group-header="{ group }">
|
|
377
|
+
<span class="text-base font-medium leading-6 text-gray-900">
|
|
378
|
+
{{ group.group }} ({{ group.rows.length }})
|
|
379
|
+
</span>
|
|
380
|
+
</template>
|
|
381
|
+
</ListView>
|
|
382
|
+
</Variant>
|
|
383
|
+
<Variant title="Cell Slot">
|
|
384
|
+
<div>
|
|
385
|
+
<ListView
|
|
386
|
+
class="h-[250px]"
|
|
387
|
+
:columns="simple_columns"
|
|
388
|
+
:rows="simple_rows"
|
|
389
|
+
:options="{
|
|
390
|
+
selectable: state.selectable,
|
|
391
|
+
showTooltip: state.showTooltip,
|
|
392
|
+
resizeColumn: state.resizeColumn,
|
|
393
|
+
emptyState: state.emptyState,
|
|
394
|
+
}"
|
|
395
|
+
row-key="id"
|
|
396
|
+
>
|
|
397
|
+
<template #cell="{ item, row, column }">
|
|
398
|
+
<Badge v-if="column.key == 'status'">{{ item }}</Badge>
|
|
399
|
+
<span class="font-medium text-gray-700" v-else>{{ item }}</span>
|
|
400
|
+
</template>
|
|
401
|
+
</ListView>
|
|
402
|
+
</div>
|
|
403
|
+
</Variant>
|
|
404
|
+
<Variant title="Empty List">
|
|
405
|
+
<div>
|
|
406
|
+
<ListView
|
|
407
|
+
class="h-[250px]"
|
|
408
|
+
:columns="simple_columns"
|
|
409
|
+
:rows="[]"
|
|
410
|
+
:options="{
|
|
411
|
+
selectable: state.selectable,
|
|
412
|
+
showTooltip: state.showTooltip,
|
|
413
|
+
resizeColumn: state.resizeColumn,
|
|
414
|
+
emptyState: state.emptyState,
|
|
415
|
+
}"
|
|
416
|
+
row-key="id"
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
</Variant>
|
|
205
420
|
|
|
206
421
|
<template #controls>
|
|
207
422
|
<HstCheckbox v-model="state.selectable" title="Selectable" />
|
|
208
423
|
<HstCheckbox v-model="state.showTooltip" title="Show tooltip" />
|
|
209
424
|
<HstCheckbox v-model="state.resizeColumn" title="Resize Column" />
|
|
425
|
+
<!-- empty state config -->
|
|
426
|
+
<HstText
|
|
427
|
+
v-model="state.emptyState.title"
|
|
428
|
+
title="Empty Title"
|
|
429
|
+
placeholder="No records found"
|
|
430
|
+
/>
|
|
431
|
+
<HstText
|
|
432
|
+
v-model="state.emptyState.description"
|
|
433
|
+
title="Empty Description"
|
|
434
|
+
placeholder="Create a new record to get started"
|
|
435
|
+
/>
|
|
210
436
|
</template>
|
|
211
437
|
</Story>
|
|
212
438
|
</template>
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
<teleport to="#melonui-popper-root">
|
|
18
18
|
<div
|
|
19
19
|
ref="popover"
|
|
20
|
-
|
|
21
|
-
class="
|
|
20
|
+
class="relative z-[100]"
|
|
21
|
+
:class="[popoverContainerClass, popoverClass]"
|
|
22
22
|
:style="{ minWidth: targetWidth ? targetWidth + 'px' : null }"
|
|
23
23
|
@mouseover="pointerOverTargetOrPopup = true"
|
|
24
24
|
@mouseleave="onMouseleave"
|
|
@@ -87,6 +87,7 @@ export default {
|
|
|
87
87
|
expose: ['open', 'close'],
|
|
88
88
|
data() {
|
|
89
89
|
return {
|
|
90
|
+
popoverContainerClass: 'body-container',
|
|
90
91
|
showPopup: false,
|
|
91
92
|
targetWidth: null,
|
|
92
93
|
pointerOverTargetOrPopup: false,
|
|
@@ -111,14 +112,35 @@ export default {
|
|
|
111
112
|
},
|
|
112
113
|
mounted() {
|
|
113
114
|
this.listener = (e) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const clickedElement = e.target
|
|
116
|
+
const reference = this.$refs.reference
|
|
117
|
+
const popoverBody = this.$refs.popover
|
|
118
|
+
const insideClick =
|
|
119
|
+
clickedElement === reference ||
|
|
120
|
+
clickedElement === popoverBody ||
|
|
121
|
+
reference?.contains(clickedElement) ||
|
|
122
|
+
popoverBody?.contains(clickedElement)
|
|
118
123
|
if (insideClick) {
|
|
119
124
|
return
|
|
120
125
|
}
|
|
121
|
-
|
|
126
|
+
|
|
127
|
+
const root = document.getElementById('melonui-popper-root')
|
|
128
|
+
const insidePopoverRoot = root.contains(clickedElement)
|
|
129
|
+
if (!insidePopoverRoot) {
|
|
130
|
+
return this.close()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const bodyClass = `.${this.popoverContainerClass}`
|
|
134
|
+
const clickedElementBody = clickedElement?.closest(bodyClass)
|
|
135
|
+
const currentPopoverBody = reference?.closest(bodyClass)
|
|
136
|
+
const isSiblingClicked =
|
|
137
|
+
clickedElementBody &&
|
|
138
|
+
currentPopoverBody &&
|
|
139
|
+
clickedElementBody === currentPopoverBody
|
|
140
|
+
|
|
141
|
+
if (isSiblingClicked) {
|
|
142
|
+
this.close()
|
|
143
|
+
}
|
|
122
144
|
}
|
|
123
145
|
if (this.hideOnBlur) {
|
|
124
146
|
document.addEventListener('click', this.listener)
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
>
|
|
11
11
|
<slot name="prefix"> </slot>
|
|
12
12
|
</div>
|
|
13
|
+
<div
|
|
14
|
+
v-if="placeholder"
|
|
15
|
+
v-show="!modelValue"
|
|
16
|
+
class="pointer-events-none absolute text-gray-500"
|
|
17
|
+
:class="[fontSizeClasses, paddingClasses]"
|
|
18
|
+
>
|
|
19
|
+
{{ placeholder }}
|
|
20
|
+
</div>
|
|
13
21
|
<select
|
|
14
22
|
:class="selectClasses"
|
|
15
23
|
:disabled="disabled"
|
|
@@ -33,7 +41,10 @@
|
|
|
33
41
|
|
|
34
42
|
<script setup lang="ts">
|
|
35
43
|
import { computed, useSlots, useAttrs } from 'vue'
|
|
36
|
-
|
|
44
|
+
|
|
45
|
+
defineOptions({
|
|
46
|
+
inheritAttrs: false,
|
|
47
|
+
})
|
|
37
48
|
|
|
38
49
|
type SelectOption =
|
|
39
50
|
| string
|
|
@@ -86,19 +97,30 @@ const textColor = computed(() => {
|
|
|
86
97
|
return props.disabled ? 'text-gray-500' : 'text-gray-800'
|
|
87
98
|
})
|
|
88
99
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
sm: 'text-base
|
|
92
|
-
md: 'text-base
|
|
93
|
-
lg: 'text-lg
|
|
94
|
-
xl: 'text-xl
|
|
100
|
+
const fontSizeClasses = computed(() => {
|
|
101
|
+
return {
|
|
102
|
+
sm: 'text-base',
|
|
103
|
+
md: 'text-base',
|
|
104
|
+
lg: 'text-lg',
|
|
105
|
+
xl: 'text-xl',
|
|
95
106
|
}[props.size]
|
|
107
|
+
})
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
const paddingClasses = computed(() => {
|
|
110
|
+
return {
|
|
111
|
+
sm: 'px-2',
|
|
112
|
+
md: 'px-2.5',
|
|
113
|
+
lg: 'px-3',
|
|
114
|
+
xl: 'px-3',
|
|
115
|
+
}[props.size]
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const selectClasses = computed(() => {
|
|
119
|
+
let sizeClasses = {
|
|
120
|
+
sm: 'rounded h-7',
|
|
121
|
+
md: 'rounded h-8',
|
|
122
|
+
lg: 'rounded-md h-10',
|
|
123
|
+
xl: 'rounded-md h-10',
|
|
102
124
|
}[props.size]
|
|
103
125
|
|
|
104
126
|
let variant = props.disabled ? 'disabled' : props.variant
|
|
@@ -118,10 +140,11 @@ const selectClasses = computed(() => {
|
|
|
118
140
|
|
|
119
141
|
return [
|
|
120
142
|
sizeClasses,
|
|
121
|
-
|
|
143
|
+
fontSizeClasses.value,
|
|
144
|
+
paddingClasses.value,
|
|
122
145
|
variantClasses,
|
|
123
146
|
textColor.value,
|
|
124
|
-
'transition-colors w-full',
|
|
147
|
+
'transition-colors w-full py-0',
|
|
125
148
|
]
|
|
126
149
|
})
|
|
127
150
|
|
package/src/index.js
CHANGED
|
@@ -41,9 +41,13 @@ export {
|
|
|
41
41
|
export { default as ListView } from './components/ListView/ListView.vue'
|
|
42
42
|
export { default as ListHeader } from './components/ListView/ListHeader.vue'
|
|
43
43
|
export { default as ListHeaderItem } from './components/ListView/ListHeaderItem.vue'
|
|
44
|
+
export { default as ListEmptyState } from './components/ListView/ListEmptyState.vue'
|
|
44
45
|
export { default as ListRows } from './components/ListView/ListRows.vue'
|
|
45
46
|
export { default as ListRow } from './components/ListView/ListRow.vue'
|
|
46
47
|
export { default as ListRowItem } from './components/ListView/ListRowItem.vue'
|
|
48
|
+
export { default as ListGroups } from './components/ListView/ListGroups.vue'
|
|
49
|
+
export { default as ListGroupHeader } from './components/ListView/ListGroupHeader.vue'
|
|
50
|
+
export { default as ListGroupRows } from './components/ListView/ListGroupRows.vue'
|
|
47
51
|
export { default as ListSelectBanner } from './components/ListView/ListSelectBanner.vue'
|
|
48
52
|
export { default as ListFooter } from './components/ListView/ListFooter.vue'
|
|
49
53
|
export { default as Toast } from './components/Toast.vue'
|
|
@@ -37,6 +37,7 @@ export function createListResource(options, vm) {
|
|
|
37
37
|
doctype: options.doctype,
|
|
38
38
|
fields: options.fields,
|
|
39
39
|
filters: options.filters,
|
|
40
|
+
orFilters: options.orFilters,
|
|
40
41
|
orderBy: options.orderBy,
|
|
41
42
|
start: options.start || 0,
|
|
42
43
|
pageLength: options.pageLength || 20,
|
|
@@ -59,6 +60,7 @@ export function createListResource(options, vm) {
|
|
|
59
60
|
doctype: out.doctype,
|
|
60
61
|
fields: out.fields,
|
|
61
62
|
filters: out.filters,
|
|
63
|
+
or_filters: out.orFilters,
|
|
62
64
|
order_by: out.orderBy,
|
|
63
65
|
start: out.start,
|
|
64
66
|
limit: out.pageLength,
|
|
@@ -71,9 +73,7 @@ export function createListResource(options, vm) {
|
|
|
71
73
|
},
|
|
72
74
|
onSuccess(data) {
|
|
73
75
|
out.hasPreviousPage = !!out.start
|
|
74
|
-
|
|
75
|
-
out.hasNextPage = false
|
|
76
|
-
}
|
|
76
|
+
out.hasNextPage = data.length < out.pageLength ? false : true
|
|
77
77
|
let pagedData
|
|
78
78
|
if (!out.start || out.start == 0) {
|
|
79
79
|
pagedData = data
|
package/src/resources/plugin.js
CHANGED
|
@@ -23,11 +23,9 @@ let createMixin = (mixinOptions) => ({
|
|
|
23
23
|
console.warn('Failed to get resource options\n\n', error)
|
|
24
24
|
out = null
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return out
|
|
27
27
|
},
|
|
28
|
-
(
|
|
29
|
-
let options = _options ? JSON.parse(_options) : null
|
|
30
|
-
let oldOptions = _oldOptions ? JSON.parse(_oldOptions) : null
|
|
28
|
+
(options, oldOptions) => {
|
|
31
29
|
if (!options) {
|
|
32
30
|
return
|
|
33
31
|
}
|
package/vite.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
|
|
4
|
-
module.exports = function proxyOptions({
|
|
4
|
+
module.exports = function proxyOptions({
|
|
5
|
+
port = 8080,
|
|
6
|
+
source = '^/(app|login|api|assets|files)',
|
|
7
|
+
} = {}) {
|
|
5
8
|
const config = getCommonSiteConfig()
|
|
6
9
|
const webserver_port = config ? config.webserver_port : 8000
|
|
7
10
|
if (!config) {
|
|
8
11
|
console.log('No common_site_config.json found, using default port 8000')
|
|
9
12
|
}
|
|
13
|
+
let proxy = {}
|
|
14
|
+
proxy[source] = {
|
|
15
|
+
target: `http://127.0.0.1:${webserver_port}`,
|
|
16
|
+
ws: true,
|
|
17
|
+
router: function (req) {
|
|
18
|
+
const site_name = req.headers.host.split(':')[0]
|
|
19
|
+
return `http://${site_name}:${webserver_port}`
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
return {
|
|
11
23
|
name: 'melonui-vite-plugin',
|
|
12
24
|
config: () => ({
|
|
13
25
|
server: {
|
|
14
26
|
port: port,
|
|
15
|
-
proxy:
|
|
16
|
-
'^/(app|login|api|assets|files)': {
|
|
17
|
-
target: `http://127.0.0.1:${webserver_port}`,
|
|
18
|
-
ws: true,
|
|
19
|
-
router: function (req) {
|
|
20
|
-
const site_name = req.headers.host.split(':')[0]
|
|
21
|
-
return `http://${site_name}:${webserver_port}`
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
},
|
|
27
|
+
proxy: proxy,
|
|
25
28
|
},
|
|
26
29
|
}),
|
|
27
30
|
}
|