@xy-planning-network/trees 0.3.4 → 0.4.0-rc
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/dist/style.css +1 -0
- package/dist/trees.es.js +8399 -0
- package/dist/trees.umd.js +10 -0
- package/dist/types/api/base.d.ts +12 -0
- package/dist/types/entry.d.ts +8 -0
- package/dist/types/helpers/Uniques.d.ts +4 -0
- package/dist/types/lib-components/forms/BaseInput.vue.d.ts +38 -0
- package/dist/types/lib-components/forms/Checkbox.vue.d.ts +22 -0
- package/dist/types/lib-components/forms/DateRangePicker.vue.d.ts +45 -0
- package/dist/types/lib-components/forms/InputHelp.vue.d.ts +22 -0
- package/dist/types/lib-components/forms/InputLabel.vue.d.ts +22 -0
- package/dist/types/lib-components/forms/MultiCheckboxes.vue.d.ts +34 -0
- package/dist/types/lib-components/forms/Radio.vue.d.ts +44 -0
- package/dist/types/lib-components/forms/Select.vue.d.ts +58 -0
- package/dist/types/lib-components/forms/TextArea.vue.d.ts +32 -0
- package/dist/types/lib-components/forms/Toggle.vue.d.ts +17 -0
- package/dist/types/lib-components/forms/YesOrNoRadio.vue.d.ts +33 -0
- package/dist/types/lib-components/index.d.ts +28 -0
- package/dist/types/lib-components/layout/DateFilter.vue.d.ts +34 -0
- package/dist/types/lib-components/layout/SidebarLayout.vue.d.ts +33 -0
- package/dist/types/lib-components/layout/StackedLayout.vue.d.ts +40 -0
- package/dist/types/lib-components/lists/Cards.vue.d.ts +23 -0
- package/dist/types/lib-components/lists/DetailList.vue.d.ts +34 -0
- package/dist/types/lib-components/lists/DownloadCell.vue.d.ts +20 -0
- package/dist/types/lib-components/lists/StaticTable.vue.d.ts +18 -0
- package/dist/types/lib-components/lists/Table.vue.d.ts +29 -0
- package/dist/types/lib-components/navigation/ActionsDropdown.vue.d.ts +26 -0
- package/dist/types/lib-components/navigation/Paginator.vue.d.ts +27 -0
- package/dist/types/lib-components/navigation/Steps.vue.d.ts +53 -0
- package/dist/types/lib-components/navigation/Tabs.vue.d.ts +36 -0
- package/dist/types/lib-components/overlays/ContentModal.vue.d.ts +24 -0
- package/dist/types/lib-components/overlays/Flash.vue.d.ts +6 -0
- package/dist/types/lib-components/overlays/Modal.vue.d.ts +51 -0
- package/dist/types/lib-components/overlays/Slideover.vue.d.ts +30 -0
- package/dist/types/lib-components/overlays/Spinner.vue.d.ts +2 -0
- package/dist/types/types/nav.d.ts +7 -0
- package/dist/types/types/table.d.ts +32 -0
- package/dist/types/types/users.d.ts +9 -0
- package/package.json +47 -66
- package/src/lib-components/forms/BaseInput.vue +51 -45
- package/src/lib-components/forms/Checkbox.vue +31 -22
- package/src/lib-components/forms/DateRangePicker.vue +56 -56
- package/src/lib-components/forms/InputHelp.vue +12 -9
- package/src/lib-components/forms/InputLabel.vue +12 -9
- package/src/lib-components/forms/MultiCheckboxes.vue +48 -44
- package/src/lib-components/forms/Radio.vue +34 -24
- package/src/lib-components/forms/Select.vue +40 -46
- package/src/lib-components/forms/TextArea.vue +23 -17
- package/src/lib-components/forms/Toggle.vue +7 -11
- package/src/lib-components/forms/YesOrNoRadio.vue +31 -27
- package/src/lib-components/layout/DateFilter.vue +31 -30
- package/src/lib-components/layout/SidebarLayout.vue +36 -51
- package/src/lib-components/layout/StackedLayout.vue +32 -55
- package/src/lib-components/lists/Cards.vue +8 -12
- package/src/lib-components/lists/DetailList.vue +83 -83
- package/src/lib-components/lists/DownloadCell.vue +8 -12
- package/src/lib-components/lists/StaticTable.vue +30 -23
- package/src/lib-components/lists/Table.vue +146 -122
- package/src/lib-components/navigation/ActionsDropdown.vue +39 -43
- package/src/lib-components/navigation/Paginator.vue +65 -80
- package/src/lib-components/navigation/Steps.vue +38 -27
- package/src/lib-components/navigation/Tabs.vue +64 -60
- package/src/lib-components/overlays/ContentModal.vue +27 -31
- package/src/lib-components/overlays/Flash.vue +85 -70
- package/src/lib-components/overlays/Modal.vue +39 -42
- package/src/lib-components/overlays/Slideover.vue +30 -35
- package/src/lib-components/overlays/Spinner.vue +51 -51
- package/src/types/env.d.ts +18 -0
- package/src/types/global.d.ts +10 -0
- package/dist/trees.esm.js +0 -10994
- package/dist/trees.min.js +0 -7
- package/dist/trees.ssr.js +0 -11669
- package/trees.d.ts +0 -43
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/vue"
|
|
3
|
+
import { DotsVerticalIcon } from "@heroicons/vue/solid"
|
|
4
|
+
import { onMounted, ref } from "vue"
|
|
5
|
+
import * as TableTypes from "../../types/table"
|
|
6
|
+
import User from "../../types/users"
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
currentUser: User
|
|
10
|
+
items: TableTypes.MenuItem[]
|
|
11
|
+
propsData: any
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const hasActionItems = ref(false)
|
|
15
|
+
|
|
16
|
+
const emitEvent = (event: string): void => {
|
|
17
|
+
window.VueBus.emit(event, props.propsData)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const show = (item: TableTypes.MenuItem): boolean => {
|
|
21
|
+
if (!item.show) return true
|
|
22
|
+
return item.show(props.propsData, props.currentUser)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onMounted(() => {
|
|
26
|
+
for (let item of props.items) {
|
|
27
|
+
if (!item.show) {
|
|
28
|
+
hasActionItems.value = true
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const showActionItem = item.show(props.propsData, props.currentUser)
|
|
33
|
+
if (showActionItem) {
|
|
34
|
+
hasActionItems.value = true
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
</script>
|
|
1
40
|
<template>
|
|
2
41
|
<Menu as="div" class="relative flex justify-end items-center">
|
|
3
42
|
<MenuButton
|
|
@@ -37,46 +76,3 @@
|
|
|
37
76
|
</transition>
|
|
38
77
|
</Menu>
|
|
39
78
|
</template>
|
|
40
|
-
|
|
41
|
-
<script lang="ts">
|
|
42
|
-
import { Options, Prop, Vue } from "vue-property-decorator";
|
|
43
|
-
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/vue";
|
|
44
|
-
import { DotsVerticalIcon } from "@heroicons/vue/solid";
|
|
45
|
-
import TableTypes from "../../types/table";
|
|
46
|
-
import UserTypes from "../../types/users";
|
|
47
|
-
|
|
48
|
-
@Options({
|
|
49
|
-
components: { DotsVerticalIcon, Menu, MenuButton, MenuItem, MenuItems },
|
|
50
|
-
name: "ActionsDropdown",
|
|
51
|
-
})
|
|
52
|
-
export default class ActionsDropdown extends Vue {
|
|
53
|
-
@Prop({ type: Object, required: true }) currentUser!: UserTypes.User;
|
|
54
|
-
@Prop({ type: Array, required: true }) items!: Array<TableTypes.MenuItem>;
|
|
55
|
-
@Prop({ type: Object, required: true }) propsData!: any;
|
|
56
|
-
|
|
57
|
-
hasActionItems = false;
|
|
58
|
-
|
|
59
|
-
mounted(): void {
|
|
60
|
-
for (let item of this.items) {
|
|
61
|
-
if (!item.show) {
|
|
62
|
-
this.hasActionItems = true;
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const showActionItem = item.show(this.propsData, this.currentUser);
|
|
67
|
-
if (showActionItem) {
|
|
68
|
-
this.hasActionItems = true;
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
emitEvent(event: string): void {
|
|
75
|
-
window.VueBus.emit(event, this.propsData);
|
|
76
|
-
}
|
|
77
|
-
show(item: TableTypes.MenuItem): boolean {
|
|
78
|
-
if (!item.show) return true;
|
|
79
|
-
return item.show(this.propsData, this.currentUser);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
</script>
|
|
@@ -1,12 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// TODO: explore this further as a pattern for exporting commonly used types
|
|
3
|
+
export interface Pagination {
|
|
4
|
+
page: number
|
|
5
|
+
perPage: number
|
|
6
|
+
totalItems: number
|
|
7
|
+
totalPages: number
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { computed, ref } from "vue"
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
modelValue: Pagination
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits<{
|
|
18
|
+
(e: "update:modelValue", pagination: Pagination): void
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const pagination = ref<Pagination>(props.modelValue)
|
|
22
|
+
|
|
23
|
+
const updateModelValue = () => {
|
|
24
|
+
emit("update:modelValue", pagination.value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const changePage = (page: number): void => {
|
|
28
|
+
pagination.value.page = page
|
|
29
|
+
updateModelValue()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const pageShortcuts = computed((): number[] => {
|
|
33
|
+
const shortcuts: number[] = []
|
|
34
|
+
|
|
35
|
+
// If total pages is less than or equal to 4, just return 1, 2, 3, 4
|
|
36
|
+
if (pagination.value.totalPages <= 4) {
|
|
37
|
+
for (let i = 0; i < pagination.value.totalPages; i++) {
|
|
38
|
+
shortcuts.push(i + 1)
|
|
39
|
+
}
|
|
40
|
+
return shortcuts
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If there are more than 3 pages left, show these
|
|
44
|
+
// e.g. [4, 5, 6, 7] when there are 8 total pages and the current page is 4
|
|
45
|
+
const pagesLeft: number = pagination.value.totalPages - pagination.value.page
|
|
46
|
+
if (pagesLeft >= 3) {
|
|
47
|
+
for (let i = 0; i < 4; i++) {
|
|
48
|
+
shortcuts.push(pagination.value.page + i)
|
|
49
|
+
}
|
|
50
|
+
return shortcuts
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// If there are less than 3 pages left, count backwards from the last page
|
|
54
|
+
// e.g. [5, 6, 7, 8] when on page 5, 6, 7, and 8 and there are 8 total pages
|
|
55
|
+
for (let i = 0; i < 4; i++) {
|
|
56
|
+
shortcuts.unshift(pagination.value.totalPages - i)
|
|
57
|
+
}
|
|
58
|
+
return shortcuts
|
|
59
|
+
})
|
|
60
|
+
</script>
|
|
1
61
|
<template>
|
|
2
62
|
<div class="px-4 flex items-center justify-between sm:px-0">
|
|
3
63
|
<div class="w-0 flex-1 flex">
|
|
4
64
|
<a
|
|
5
65
|
href="#"
|
|
6
66
|
class="-mt-px border-t-2 border-transparent pt-4 pr-1 inline-flex items-center text-sm leading-5 font-medium focus:outline-none focus:text-gray-700 focus:border-gray-400"
|
|
7
|
-
@click.prevent="changePage(
|
|
67
|
+
@click.prevent="changePage(pagination.page - 1)"
|
|
8
68
|
:class="
|
|
9
|
-
|
|
69
|
+
pagination.page == 1
|
|
10
70
|
? 'text-gray-500 cursor-not-allowed pointer-events-none'
|
|
11
71
|
: 'text-gray-700 hover:text-gray-900 hover:border-gray-300'
|
|
12
72
|
"
|
|
@@ -30,7 +90,7 @@
|
|
|
30
90
|
:key="i"
|
|
31
91
|
v-text="i"
|
|
32
92
|
:class="
|
|
33
|
-
|
|
93
|
+
pagination.page === i
|
|
34
94
|
? 'border-blue-500 text-blue-600 focus:outline-none focus:text-blue-800 focus:border-blue-700'
|
|
35
95
|
: 'border-transparent text-gray-700 hover:text-gray-900 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-400'
|
|
36
96
|
"
|
|
@@ -42,9 +102,9 @@
|
|
|
42
102
|
<a
|
|
43
103
|
href="#"
|
|
44
104
|
class="-mt-px border-t-2 border-transparent pt-4 pl-1 inline-flex items-center text-sm leading-5 font-medium focus:outline-none focus:text-gray-700 focus:border-gray-400"
|
|
45
|
-
@click.prevent="changePage(
|
|
105
|
+
@click.prevent="changePage(pagination.page + 1)"
|
|
46
106
|
:class="
|
|
47
|
-
|
|
107
|
+
pagination.page >= pagination.totalPages
|
|
48
108
|
? 'text-gray-500 cursor-not-allowed pointer-events-none'
|
|
49
109
|
: 'text-gray-700 hover:text-gray-900 hover:border-gray-300'
|
|
50
110
|
"
|
|
@@ -61,78 +121,3 @@
|
|
|
61
121
|
</div>
|
|
62
122
|
</div>
|
|
63
123
|
</template>
|
|
64
|
-
|
|
65
|
-
<script lang="ts">
|
|
66
|
-
import { Emit, Options, Prop, Vue } from "vue-property-decorator";
|
|
67
|
-
|
|
68
|
-
@Options({ name: "Paginator" })
|
|
69
|
-
export default class Paginator extends Vue {
|
|
70
|
-
@Prop({ type: Object, required: true }) modelValue!: {
|
|
71
|
-
page: number;
|
|
72
|
-
perPage: number;
|
|
73
|
-
totalItems: number;
|
|
74
|
-
totalPages: number;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
@Emit("update:modelValue")
|
|
78
|
-
updateModelValue(): {
|
|
79
|
-
page: number;
|
|
80
|
-
perPage: number;
|
|
81
|
-
totalItems: number;
|
|
82
|
-
totalPages: number;
|
|
83
|
-
} {
|
|
84
|
-
return this.modelValue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
changePage(page: number): void {
|
|
88
|
-
this.modelValue.page = page;
|
|
89
|
-
this.updateModelValue();
|
|
90
|
-
}
|
|
91
|
-
changePerPage(parent: HTMLElement, perPage: number): void {
|
|
92
|
-
parent.blur();
|
|
93
|
-
this.modelValue.page = 1;
|
|
94
|
-
this.modelValue.perPage = perPage;
|
|
95
|
-
this.updateModelValue();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
get endingItem(): number {
|
|
99
|
-
const end = this.modelValue.page * this.modelValue.perPage;
|
|
100
|
-
return end > this.modelValue.totalItems ? this.modelValue.totalItems : end;
|
|
101
|
-
}
|
|
102
|
-
get pageShortcuts(): number[] {
|
|
103
|
-
const shortcuts: number[] = [];
|
|
104
|
-
|
|
105
|
-
// If total pages is less than or equal to 4, just return 1, 2, 3, 4
|
|
106
|
-
if (this.modelValue.totalPages <= 4) {
|
|
107
|
-
for (let i = 0; i < this.modelValue.totalPages; i++) {
|
|
108
|
-
shortcuts.push(i + 1);
|
|
109
|
-
}
|
|
110
|
-
return shortcuts;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// If there are more than 3 pages left, show these
|
|
114
|
-
// e.g. [4, 5, 6, 7] when there are 8 total pages and the current page is 4
|
|
115
|
-
const pagesLeft: number = this.modelValue.totalPages - this.modelValue.page;
|
|
116
|
-
if (pagesLeft >= 3) {
|
|
117
|
-
for (let i = 0; i < 4; i++) {
|
|
118
|
-
shortcuts.push(this.modelValue.page + i);
|
|
119
|
-
}
|
|
120
|
-
return shortcuts;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// If there are less than 3 pages left, count backwards from the last page
|
|
124
|
-
// e.g. [5, 6, 7, 8] when on page 5, 6, 7, and 8 and there are 8 total pages
|
|
125
|
-
for (let i = 0; i < 4; i++) {
|
|
126
|
-
shortcuts.unshift(this.modelValue.totalPages - i);
|
|
127
|
-
}
|
|
128
|
-
return shortcuts;
|
|
129
|
-
}
|
|
130
|
-
get startingItem(): number {
|
|
131
|
-
const start =
|
|
132
|
-
this.modelValue.page * this.modelValue.perPage -
|
|
133
|
-
this.modelValue.perPage +
|
|
134
|
-
1;
|
|
135
|
-
return this.modelValue.totalItems === 0 ? 0 : start;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
</script>
|
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// TODO: think about whether there is value in updating and emiting step with next/previous
|
|
3
|
+
// TODO: add to docs
|
|
4
|
+
|
|
5
|
+
withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
hideActions?: boolean
|
|
8
|
+
hidePrevious?: boolean
|
|
9
|
+
nextText?: string
|
|
10
|
+
previousText?: string
|
|
11
|
+
step: number
|
|
12
|
+
total: number
|
|
13
|
+
}>(),
|
|
14
|
+
{
|
|
15
|
+
hideActions: false,
|
|
16
|
+
hidePrevious: false,
|
|
17
|
+
nextText: "Next",
|
|
18
|
+
previousText: "Back",
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits(["next", "previous"])
|
|
23
|
+
|
|
24
|
+
const next = (): void => {
|
|
25
|
+
emit("next")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const previous = (): void => {
|
|
29
|
+
emit("previous")
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
1
32
|
<template>
|
|
2
33
|
<div>
|
|
3
34
|
<nav class="flex items-center justify-center space-x-8">
|
|
@@ -32,41 +63,21 @@
|
|
|
32
63
|
|
|
33
64
|
<div class="flex flex-shrink-0" v-if="!hideActions">
|
|
34
65
|
<span class="inline-flex rounded-md shadow-sm" v-if="!hidePrevious">
|
|
35
|
-
<button
|
|
36
|
-
|
|
37
|
-
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
class="xy-btn-white"
|
|
69
|
+
@click="previous"
|
|
70
|
+
v-text="previousText"
|
|
71
|
+
></button>
|
|
38
72
|
</span>
|
|
39
73
|
<span class="ml-3 inline-flex rounded-md shadow-sm">
|
|
40
74
|
<button
|
|
41
75
|
type="button"
|
|
42
76
|
class="xy-btn"
|
|
43
77
|
@click="next"
|
|
44
|
-
v-text="nextText
|
|
78
|
+
v-text="nextText"
|
|
45
79
|
></button>
|
|
46
80
|
</span>
|
|
47
81
|
</div>
|
|
48
82
|
</div>
|
|
49
83
|
</template>
|
|
50
|
-
|
|
51
|
-
<script lang="ts">
|
|
52
|
-
import { Options, Emit, Prop, Vue } from "vue-property-decorator";
|
|
53
|
-
|
|
54
|
-
@Options({ name: "Steps" })
|
|
55
|
-
export default class Steps extends Vue {
|
|
56
|
-
@Prop({ type: Boolean, required: false }) hideActions?: boolean;
|
|
57
|
-
@Prop({ type: Boolean, required: false }) hidePrevious?: boolean;
|
|
58
|
-
@Prop({ type: String, required: false }) nextText?: string;
|
|
59
|
-
@Prop({ type: Number, required: true }) step!: number;
|
|
60
|
-
@Prop({ type: Number, required: true }) total!: number;
|
|
61
|
-
|
|
62
|
-
@Emit()
|
|
63
|
-
next(): void {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@Emit()
|
|
68
|
-
previous(): void {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
</script>
|
|
@@ -1,3 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue"
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
modelValue: string
|
|
7
|
+
pillDesign?: boolean
|
|
8
|
+
tabs: Array<{
|
|
9
|
+
label: string
|
|
10
|
+
value: string
|
|
11
|
+
}>
|
|
12
|
+
}>(),
|
|
13
|
+
{
|
|
14
|
+
pillDesign: false,
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits<{
|
|
19
|
+
(e: "update:modelValue", val: string): void
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const updateModelValue = (modelValue: string): void => {
|
|
23
|
+
emit("update:modelValue", modelValue)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const classes = (currentTab: string, pastFirstTab: boolean): string => {
|
|
27
|
+
let c = ""
|
|
28
|
+
|
|
29
|
+
if (props.pillDesign) {
|
|
30
|
+
c =
|
|
31
|
+
"px-12 py-2 font-semibold text-md leading-5 rounded-t-md focus:outline-none "
|
|
32
|
+
|
|
33
|
+
if (props.modelValue === currentTab) {
|
|
34
|
+
c = c + "focus:bg-white text-gray-700 bg-white border-b-2 border-blue-500"
|
|
35
|
+
} else {
|
|
36
|
+
c =
|
|
37
|
+
c +
|
|
38
|
+
"text-gray-700 hover:text-gray-900 focus:text-gray-900 focus:bg-gray-100 border border-gray-200"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return c
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
c =
|
|
45
|
+
"px-1 py-4 text-sm font-semibold border-b-2 whitespace-nowrap leading-5 focus:outline-none "
|
|
46
|
+
if (props.modelValue === currentTab) {
|
|
47
|
+
c =
|
|
48
|
+
c +
|
|
49
|
+
"border-blue-500 text-xy-blue focus:text-blue-800 focus:border-blue-700"
|
|
50
|
+
} else {
|
|
51
|
+
c =
|
|
52
|
+
c +
|
|
53
|
+
"border-transparent text-gray-700 hover:text-gray-900 hover:border-gray-300 focus:text-gray-900 focus:border-gray-300"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (pastFirstTab) c = c + " ml-8"
|
|
57
|
+
|
|
58
|
+
return c
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const notPillDesign = computed((): boolean => {
|
|
62
|
+
return !props.pillDesign
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
1
65
|
<template>
|
|
2
66
|
<div>
|
|
3
67
|
<div class="sm:hidden" :class="{ 'mb-4': pillDesign }">
|
|
@@ -26,63 +90,3 @@
|
|
|
26
90
|
</div>
|
|
27
91
|
</div>
|
|
28
92
|
</template>
|
|
29
|
-
|
|
30
|
-
<script lang="ts">
|
|
31
|
-
import { Emit, Options, Prop, Vue } from "vue-property-decorator";
|
|
32
|
-
|
|
33
|
-
@Options({ name: "Tabs" })
|
|
34
|
-
export default class Tabs extends Vue {
|
|
35
|
-
@Prop({ type: String, required: true }) modelValue!: string;
|
|
36
|
-
@Prop({ type: Boolean, required: true }) pillDesign?: boolean;
|
|
37
|
-
@Prop({ type: Array, required: true }) tabs!: Array<{
|
|
38
|
-
label: string;
|
|
39
|
-
value: string;
|
|
40
|
-
}>;
|
|
41
|
-
|
|
42
|
-
@Emit("update:modelValue")
|
|
43
|
-
updateModelValue(modelValue: string): string {
|
|
44
|
-
return modelValue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
classes(currentTab: string, pastFirstTab: boolean): string {
|
|
48
|
-
let c = "";
|
|
49
|
-
|
|
50
|
-
if (this.pillDesign) {
|
|
51
|
-
c =
|
|
52
|
-
"px-12 py-2 font-semibold text-md leading-5 rounded-t-md focus:outline-none ";
|
|
53
|
-
|
|
54
|
-
if (this.modelValue === currentTab) {
|
|
55
|
-
c =
|
|
56
|
-
c +
|
|
57
|
-
"focus:bg-white text-gray-700 bg-white border-b-2 border-blue-500";
|
|
58
|
-
} else {
|
|
59
|
-
c =
|
|
60
|
-
c +
|
|
61
|
-
"text-gray-700 hover:text-gray-900 focus:text-gray-900 focus:bg-gray-100 border border-gray-200";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return c;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
c =
|
|
68
|
-
"px-1 py-4 text-sm font-semibold border-b-2 whitespace-nowrap leading-5 focus:outline-none ";
|
|
69
|
-
if (this.modelValue === currentTab) {
|
|
70
|
-
c =
|
|
71
|
-
c +
|
|
72
|
-
"border-blue-500 text-xy-blue focus:text-blue-800 focus:border-blue-700";
|
|
73
|
-
} else {
|
|
74
|
-
c =
|
|
75
|
-
c +
|
|
76
|
-
"border-transparent text-gray-700 hover:text-gray-900 hover:border-gray-300 focus:text-gray-900 focus:border-gray-300";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (pastFirstTab) c = c + " ml-8";
|
|
80
|
-
|
|
81
|
-
return c;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get notPillDesign(): boolean {
|
|
85
|
-
return !this.pillDesign;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
</script>
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogOverlay,
|
|
5
|
+
DialogTitle,
|
|
6
|
+
TransitionChild,
|
|
7
|
+
TransitionRoot,
|
|
8
|
+
} from "@headlessui/vue"
|
|
9
|
+
|
|
10
|
+
withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
modelValue: boolean
|
|
13
|
+
title?: string
|
|
14
|
+
}>(),
|
|
15
|
+
{
|
|
16
|
+
title: "",
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
(e: "update:modelValue", val: boolean): void
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
const updateModelValue = (value: boolean) => {
|
|
25
|
+
emit("update:modelValue", value)
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
1
28
|
<template>
|
|
2
29
|
<TransitionRoot as="template" :show="modelValue">
|
|
3
30
|
<Dialog
|
|
@@ -66,34 +93,3 @@
|
|
|
66
93
|
</Dialog>
|
|
67
94
|
</TransitionRoot>
|
|
68
95
|
</template>
|
|
69
|
-
|
|
70
|
-
<script lang="ts">
|
|
71
|
-
import { Emit, Options, Prop, Vue } from "vue-property-decorator";
|
|
72
|
-
import {
|
|
73
|
-
Dialog,
|
|
74
|
-
DialogOverlay,
|
|
75
|
-
DialogTitle,
|
|
76
|
-
TransitionChild,
|
|
77
|
-
TransitionRoot,
|
|
78
|
-
} from "@headlessui/vue";
|
|
79
|
-
|
|
80
|
-
@Options({
|
|
81
|
-
components: {
|
|
82
|
-
Dialog,
|
|
83
|
-
DialogOverlay,
|
|
84
|
-
DialogTitle,
|
|
85
|
-
TransitionChild,
|
|
86
|
-
TransitionRoot,
|
|
87
|
-
},
|
|
88
|
-
name: "ContentModal",
|
|
89
|
-
})
|
|
90
|
-
export default class ContentModal extends Vue {
|
|
91
|
-
@Prop({ type: Boolean, required: true }) modelValue!: boolean;
|
|
92
|
-
@Prop({ type: String, required: false }) title?: string;
|
|
93
|
-
|
|
94
|
-
@Emit("update:modelValue")
|
|
95
|
-
updateModelValue(value: boolean): boolean {
|
|
96
|
-
return value;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
</script>
|
|
@@ -1,3 +1,87 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface Flash {
|
|
3
|
+
type?: string
|
|
4
|
+
message: string
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { onMounted, ref } from "vue"
|
|
9
|
+
|
|
10
|
+
// TODO: spk this might benefit from the composition api to avoid race conditions where a flash is requested before the component is mounted.
|
|
11
|
+
|
|
12
|
+
const flashes = ref<Flash[]>([])
|
|
13
|
+
const flashTypeBorderClass = {
|
|
14
|
+
warning: "border-orange-500",
|
|
15
|
+
error: "border-red-500",
|
|
16
|
+
info: "border-blue-500",
|
|
17
|
+
success: "border-green-500",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getFlashClass = (flash: Flash): string => {
|
|
21
|
+
const availableTypes = Object.keys(flashTypeBorderClass)
|
|
22
|
+
// default the flash type to "info" if no flash type or an unsupported flash.type is found
|
|
23
|
+
const type =
|
|
24
|
+
flash?.type && availableTypes.includes(flash.type)
|
|
25
|
+
? (flash.type as "warning" | "error" | "info" | "success")
|
|
26
|
+
: "info"
|
|
27
|
+
return flashTypeBorderClass[type]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const remove = (flash: Flash): void => {
|
|
31
|
+
let index = 0
|
|
32
|
+
for (const f of flashes.value) {
|
|
33
|
+
if (flash.message === f.message) {
|
|
34
|
+
flashes.value.splice(index, 1)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
index++
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const renderFlash = (flash: Flash): void => {
|
|
41
|
+
flashes.value.push(flash)
|
|
42
|
+
// Super simple flash implementation. This could get "smarter" by adding an
|
|
43
|
+
// id to the flash object, and then searching for the specific flash in the
|
|
44
|
+
// array and splicing, instead of simply doing a pop().
|
|
45
|
+
setTimeout(
|
|
46
|
+
(flashes: Flash[]) => {
|
|
47
|
+
flashes.pop()
|
|
48
|
+
},
|
|
49
|
+
10000,
|
|
50
|
+
flashes.value
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const renderGenericError = (email: string): void => {
|
|
55
|
+
renderFlash({
|
|
56
|
+
type: "error",
|
|
57
|
+
message:
|
|
58
|
+
"Whoops! Something went wrong, please reach out to " +
|
|
59
|
+
`<a class="underline text-xy-blue" href="mailto:${email}">${email}</a>` +
|
|
60
|
+
" if the issue persists.",
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onMounted(() => {
|
|
65
|
+
window.VueBus.on("Flash-show-message", (flash) => {
|
|
66
|
+
renderFlash(flash)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
window.VueBus.on("Flash-show-generic-error", (email) => {
|
|
70
|
+
renderGenericError(email)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
if (window.Flashes) {
|
|
74
|
+
for (const flash of window.Flashes) {
|
|
75
|
+
if (typeof flash.type === "undefined") {
|
|
76
|
+
const values: string[] = flash.message.split(": ")
|
|
77
|
+
renderFlash({ type: values[0], message: values[1] })
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
renderFlash({ type: flash.type, message: flash.message })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
</script>
|
|
1
85
|
<template>
|
|
2
86
|
<div
|
|
3
87
|
class="fixed inset-0 flex flex-col items-end justify-end px-4 py-6 pointer-events-none sm:p-6 z-40"
|
|
@@ -16,7 +100,7 @@
|
|
|
16
100
|
v-for="(flash, idx) in flashes"
|
|
17
101
|
:key="flash.message"
|
|
18
102
|
class="bg-white shadow-lg rounded-lg pointer-events-auto border-t-4 transform"
|
|
19
|
-
:class="[{ 'mt-2': idx > 0 },
|
|
103
|
+
:class="[{ 'mt-2': idx > 0 }, getFlashClass(flash)]"
|
|
20
104
|
>
|
|
21
105
|
<div
|
|
22
106
|
class="rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden"
|
|
@@ -50,72 +134,3 @@
|
|
|
50
134
|
</transition-group>
|
|
51
135
|
</div>
|
|
52
136
|
</template>
|
|
53
|
-
|
|
54
|
-
<script lang="ts">
|
|
55
|
-
import { Options, Vue } from "vue-property-decorator";
|
|
56
|
-
|
|
57
|
-
@Options({ name: "Flash" })
|
|
58
|
-
export default class Flash extends Vue {
|
|
59
|
-
flashes: Array<{ type: string; message: string }> = [];
|
|
60
|
-
flashTypeBorderClass = {
|
|
61
|
-
warning: "border-orange-500",
|
|
62
|
-
error: "border-red-500",
|
|
63
|
-
info: "border-blue-500",
|
|
64
|
-
success: "border-green-500",
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
mounted(): void {
|
|
68
|
-
window.VueBus.on("Flash-show-message", (flash) => {
|
|
69
|
-
this.renderFlash(flash);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
window.VueBus.on("Flash-show-generic-error", (email) => {
|
|
73
|
-
this.renderGenericError(email);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
if (window.Flashes) {
|
|
77
|
-
for (const flash of window.Flashes) {
|
|
78
|
-
if (typeof flash.type === "undefined") {
|
|
79
|
-
const values: string[] = flash.message.split(": ");
|
|
80
|
-
this.renderFlash({ type: values[0], message: values[1] });
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
this.renderFlash({ type: flash.type, message: flash.message });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
remove(flash: { type: string; message: string }): void {
|
|
89
|
-
let index = 0;
|
|
90
|
-
for (const f of this.flashes) {
|
|
91
|
-
if (flash.message === f.message) {
|
|
92
|
-
this.flashes.splice(index, 1);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
index++;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
renderFlash(flash: { type: string; message: string }): void {
|
|
99
|
-
this.flashes.push(flash);
|
|
100
|
-
// Super simple flash implementation. This could get "smarter" by adding an
|
|
101
|
-
// id to the flash object, and then searching for the specific flash in the
|
|
102
|
-
// array and splicing, instead of simply doing a pop().
|
|
103
|
-
setTimeout(
|
|
104
|
-
(flashes: Array<{ type: string; message: string }>) => {
|
|
105
|
-
flashes.pop();
|
|
106
|
-
},
|
|
107
|
-
10000,
|
|
108
|
-
this.flashes
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
renderGenericError(email: string): void {
|
|
112
|
-
this.renderFlash({
|
|
113
|
-
type: "error",
|
|
114
|
-
message:
|
|
115
|
-
"Whoops! Something went wrong, please reach out to " +
|
|
116
|
-
`<a class="underline text-xy-blue" href="mailto:${email}">${email}</a>` +
|
|
117
|
-
" if the issue persists.",
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
</script>
|