-
Notifications
You must be signed in to change notification settings - Fork 296
/
BalStack.vue
140 lines (136 loc) · 3.83 KB
/
BalStack.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<script lang="ts">
import { defineComponent, PropType, h } from 'vue';
type Spacing = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | 'none';
type Alignment = 'center' | 'start' | 'end' | 'between';
/**
* Maps discrete spacing types to a tailwind spacing tier
*/
const SpacingMap: Record<Spacing, number> = {
xs: 1,
sm: 2,
base: 4,
lg: 8,
xl: 12,
'2xl': 16,
none: 0
};
export default defineComponent({
props: {
/**
* Stacked top down
*/
vertical: { type: Boolean, default: () => false },
/**
* Stacked left to right
*/
horizontal: { type: Boolean, default: () => false },
spacing: {
type: String as PropType<Spacing>,
default: () => 'base'
},
/**
* Show a hairline border after each stack element
*/
withBorder: {
type: Boolean,
default: () => false
},
/**
* Flex align prop
*/
align: {
type: String as PropType<Alignment>
},
/**
* Flex justify prop
*/
justify: {
type: String as PropType<Alignment>
},
/**
* Will cause children of the stack to occupy
* as much space as possible.
*/
expandChildren: {
type: Boolean,
default: () => false
}
},
setup(props, { slots, attrs }) {
return {
slotsWithContent: [],
slots,
attrs
};
},
render() {
const spacingType = this.vertical ? 'mb' : 'mr';
const borderType = this.vertical ? 'b' : 'r';
const widthClass = this.expandChildren ? 'w-full' : '';
const borderClass = this.withBorder ? `border-${borderType}` : '';
const stackNodeClass = `dark:border-gray-600 ${spacingType}-${
SpacingMap[this.spacing]
} ${borderClass} ${widthClass}`;
// @ts-ignore
const vNodes = this.$slots.default() || [];
// if a childs 'value' is 'v-if', it is not visible so filter it out
// to not cause an empty node to render with margin
const children = vNodes.filter(vNode => vNode.children !== 'v-if');
// need to apply margin and decorator classes to all children
const styledChildren = children.map((child, childIndex) => {
let styledNestedChildren = child.children;
// a child can have an array of nested children, this is when
// those children are rendered as part of a 'v-for directive'
if (Array.isArray(styledNestedChildren)) {
// and those children can be nullish too
const nonNullishChildren = styledNestedChildren.filter(
nestedChild => nestedChild !== undefined || nestedChild !== null
);
styledNestedChildren = nonNullishChildren.map(
(nestedChild, nestedChildIndex) => {
//@ts-ignore
return h(nestedChild, {
class:
nestedChildIndex !== nonNullishChildren.length - 1
? stackNodeClass
: null
});
}
);
return h(
child,
{
class: childIndex !== children.length - 1 ? stackNodeClass : null
},
[styledNestedChildren]
);
}
return h(child, {
class: childIndex !== children.length - 1 ? stackNodeClass : null
});
});
return h(
'div',
{
attrs: this.$attrs,
class: [
'flex',
{
'flex-row': this.horizontal,
'flex-col': this.vertical,
'items-center': this.align === 'center',
'items-start': this.align === 'start',
'items-end': this.align === 'end',
'items-between': this.align === 'between',
'justify-center': this.justify === 'center',
'justify-start': this.justify === 'start',
'justify-end': this.justify === 'end',
'justify-between': this.justify === 'between'
}
]
},
[styledChildren]
);
}
});
</script>