@bobbykim/manguito-theme 0.3.0 → 0.4.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/index.ts +8 -1
- package/lib/collapse/Collapse.vue +85 -0
- package/lib/collapse/index.types.ts +3 -0
- package/lib/directives/index.ts +57 -0
- package/lib/dropdown/DropdownContainer.vue +47 -0
- package/lib/dropdown/DropdownContent.vue +70 -0
- package/lib/dropdown/index.types.ts +4 -0
- package/package.json +2 -2
- package/theme/index.ts +9 -0
- package/theme/theme.ts +21 -1
- package/themeVariables.cjs +11 -0
package/index.ts
CHANGED
|
@@ -5,9 +5,13 @@ import GenerateMCLClass, {
|
|
|
5
5
|
scaleType,
|
|
6
6
|
} from './theme'
|
|
7
7
|
import type { ClassType, InputType } from './theme/index.js'
|
|
8
|
+
// base components
|
|
9
|
+
import DropdownContainer from './lib/dropdown/DropdownContainer.vue'
|
|
10
|
+
import DropdownContent from './lib/dropdown/DropdownContent.vue'
|
|
11
|
+
import Collapse from './lib/collapse/Collapse.vue'
|
|
12
|
+
import type { CollapseEvent } from './lib/collapse/index.types'
|
|
8
13
|
|
|
9
14
|
const generateClass = (type: ClassType, value: InputType): string => {
|
|
10
|
-
console.log('running generate class')
|
|
11
15
|
const mclGenerate = new GenerateMCLClass(type, value)
|
|
12
16
|
if ((<string[]>(<unknown>colorType)).includes(type)) {
|
|
13
17
|
return mclGenerate.generateColorClass()
|
|
@@ -26,3 +30,6 @@ const generateClass = (type: ClassType, value: InputType): string => {
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export default generateClass
|
|
33
|
+
|
|
34
|
+
export { DropdownContainer, DropdownContent, Collapse, CollapseEvent }
|
|
35
|
+
export * from './lib/directives'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
|
|
3
|
+
import type { CollapseEvent } from './index.types'
|
|
4
|
+
const props = withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
collapseId: string
|
|
7
|
+
accordion?: string
|
|
8
|
+
className?: string
|
|
9
|
+
visible?: boolean
|
|
10
|
+
}>(),
|
|
11
|
+
{
|
|
12
|
+
className: '',
|
|
13
|
+
visible: false,
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
const emit = defineEmits(['toggle'])
|
|
17
|
+
const toggle = ref<boolean>(props.visible)
|
|
18
|
+
const contentRef = ref<HTMLAreaElement>()
|
|
19
|
+
const slotHeight = ref<number>()
|
|
20
|
+
|
|
21
|
+
const handleToggleEvent = (e: Event): void => {
|
|
22
|
+
toggle.value = !toggle.value
|
|
23
|
+
emit('toggle', { ...e, visible: toggle.value } as CollapseEvent)
|
|
24
|
+
}
|
|
25
|
+
const initObserver = (): ResizeObserver => {
|
|
26
|
+
const observer = new ResizeObserver(() => {
|
|
27
|
+
if (contentRef.value) {
|
|
28
|
+
slotHeight.value = contentRef.value.scrollHeight
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
slotHeight.value = 0
|
|
32
|
+
})
|
|
33
|
+
return observer
|
|
34
|
+
}
|
|
35
|
+
const slotHeightVal = computed(() => {
|
|
36
|
+
return {
|
|
37
|
+
height: contentRef.value && toggle.value ? slotHeight.value + 'px' : '0px',
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
watch(
|
|
41
|
+
() => props.visible,
|
|
42
|
+
(newValue) => {
|
|
43
|
+
toggle.value = newValue
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
// lifecycle hooks
|
|
47
|
+
onMounted(() => {
|
|
48
|
+
if (typeof window !== undefined) {
|
|
49
|
+
initObserver().observe(contentRef.value as Element)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
onBeforeUnmount(() => {
|
|
53
|
+
initObserver().disconnect()
|
|
54
|
+
})
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<div>
|
|
59
|
+
<button
|
|
60
|
+
:id="collapseId"
|
|
61
|
+
@click="handleToggleEvent"
|
|
62
|
+
class="hidden"
|
|
63
|
+
:accordion="accordion"
|
|
64
|
+
:visible="toggle"
|
|
65
|
+
></button>
|
|
66
|
+
<div
|
|
67
|
+
class="overflow-hidden transition-[height] duration-500"
|
|
68
|
+
:style="slotHeightVal"
|
|
69
|
+
:class="[toggle ? 'ease-in' : 'ease-out']"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
ref="contentRef"
|
|
73
|
+
class="transition-opacity duration-500"
|
|
74
|
+
:class="[
|
|
75
|
+
toggle ? 'opacity-100 ease-in' : 'opacity-0 ease-out',
|
|
76
|
+
className,
|
|
77
|
+
]"
|
|
78
|
+
>
|
|
79
|
+
<slot />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<style scoped></style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Directive } from 'vue'
|
|
2
|
+
|
|
3
|
+
export const vClickOutside: Directive = {
|
|
4
|
+
mounted(el, binding) {
|
|
5
|
+
el.__ClickOutsideHandler = (event: Event) => {
|
|
6
|
+
if (!(el === event.target || el.contains(event.target))) {
|
|
7
|
+
binding.value(event)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
document.body.addEventListener('click', el.__ClickOutsideHandler)
|
|
11
|
+
},
|
|
12
|
+
unmounted(el) {
|
|
13
|
+
document.body.removeEventListener('click', el.__ClickOutsideHandler)
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const vCollapse: Directive = {
|
|
18
|
+
mounted(el, binding) {
|
|
19
|
+
el.__HandleToggler = (event: Event): void => {
|
|
20
|
+
const eTarget = event.target as HTMLElement
|
|
21
|
+
let target: HTMLElement
|
|
22
|
+
if (eTarget.tagName === 'A') {
|
|
23
|
+
const targetId = eTarget.getAttribute('href')
|
|
24
|
+
const formattedTargetId =
|
|
25
|
+
targetId?.charAt(0) === '#' ? targetId.substring(1) : targetId
|
|
26
|
+
target = document.getElementById(
|
|
27
|
+
formattedTargetId as string
|
|
28
|
+
) as HTMLElement
|
|
29
|
+
} else {
|
|
30
|
+
target = document.getElementById(binding.arg as string) as HTMLElement
|
|
31
|
+
}
|
|
32
|
+
if (!target.hasAttribute('accordion')) {
|
|
33
|
+
// run when it is not accordion component case
|
|
34
|
+
target.click()
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const accordionName = target.getAttribute('accordion')
|
|
38
|
+
const accordionList: NodeListOf<HTMLElement> = document.querySelectorAll(
|
|
39
|
+
`button[accordion="${accordionName}"]`
|
|
40
|
+
)
|
|
41
|
+
// loop through each accordion items and handle reaction for each case
|
|
42
|
+
for (let i = 0; i < accordionList.length; i++) {
|
|
43
|
+
const isVisible = accordionList[i].getAttribute('visible')
|
|
44
|
+
if (accordionList[i].id !== target.id && isVisible === 'true') {
|
|
45
|
+
accordionList[i].click()
|
|
46
|
+
}
|
|
47
|
+
if (accordionList[i].id === target.id) {
|
|
48
|
+
accordionList[i].click()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
el.addEventListener('click', el.__HandleToggler)
|
|
53
|
+
},
|
|
54
|
+
unmounted(el) {
|
|
55
|
+
el.removeEventListener('click', el.__HandleToggler)
|
|
56
|
+
},
|
|
57
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive, provide, ref, onMounted } from 'vue'
|
|
3
|
+
import type { InjectType } from './index.types'
|
|
4
|
+
import { vClickOutside } from '../directives'
|
|
5
|
+
|
|
6
|
+
const emit = defineEmits(['click-outside', 'toggle'])
|
|
7
|
+
const dropdownTarget = ref()
|
|
8
|
+
const dropdownState = reactive<InjectType>({
|
|
9
|
+
active: false,
|
|
10
|
+
buttonHeight: 0,
|
|
11
|
+
})
|
|
12
|
+
provide('dropdownState', dropdownState)
|
|
13
|
+
const toggle = (e: Event): void => {
|
|
14
|
+
dropdownState.active = !dropdownState.active
|
|
15
|
+
emit('toggle', e)
|
|
16
|
+
}
|
|
17
|
+
const onClickOutside = (e: Event): void => {
|
|
18
|
+
dropdownState.active = false
|
|
19
|
+
emit('click-outside', e)
|
|
20
|
+
}
|
|
21
|
+
const onEscape = (): void => {
|
|
22
|
+
dropdownState.active = false
|
|
23
|
+
}
|
|
24
|
+
onMounted(() => {
|
|
25
|
+
if (typeof window !== undefined) {
|
|
26
|
+
dropdownState.buttonHeight = dropdownTarget.value.scrollHeight
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div
|
|
33
|
+
class="relative"
|
|
34
|
+
ref="dropdownTarget"
|
|
35
|
+
v-click-outside="onClickOutside"
|
|
36
|
+
@keyup.esc="onEscape"
|
|
37
|
+
>
|
|
38
|
+
<slot
|
|
39
|
+
name="toggler"
|
|
40
|
+
:toggle="toggle"
|
|
41
|
+
:dropdown-state="dropdownState.active"
|
|
42
|
+
/>
|
|
43
|
+
<slot></slot>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, inject, Transition } from 'vue'
|
|
3
|
+
import { InjectType } from './index.types'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
className?: string
|
|
8
|
+
}>(),
|
|
9
|
+
{
|
|
10
|
+
className: '',
|
|
11
|
+
}
|
|
12
|
+
)
|
|
13
|
+
const contentRef = ref<HTMLAreaElement>()
|
|
14
|
+
const dropdownState = inject<InjectType>('dropdownState', {
|
|
15
|
+
active: false,
|
|
16
|
+
buttonHeight: 0,
|
|
17
|
+
})
|
|
18
|
+
const active = computed<boolean>(() => {
|
|
19
|
+
return dropdownState.active
|
|
20
|
+
})
|
|
21
|
+
const handleItemClick = () => {
|
|
22
|
+
dropdownState.active = false
|
|
23
|
+
}
|
|
24
|
+
const buttonHeight = computed(() => {
|
|
25
|
+
return { '--button-height': `${dropdownState.buttonHeight}px` }
|
|
26
|
+
})
|
|
27
|
+
const dropdownDirection = computed<string | undefined>(() => {
|
|
28
|
+
if (typeof window === undefined) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
if (!dropdownState.active || !contentRef.value) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
window.innerHeight - contentRef.value.getBoundingClientRect().bottom <
|
|
36
|
+
0
|
|
37
|
+
) {
|
|
38
|
+
return 'dropup-bottom'
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<transition name="dropdown-content">
|
|
45
|
+
<div
|
|
46
|
+
v-if="active"
|
|
47
|
+
ref="contentRef"
|
|
48
|
+
class="absolute my-2xs max-w-[14rem] w-max overflow-hidden"
|
|
49
|
+
:class="[dropdownDirection, className]"
|
|
50
|
+
:style="buttonHeight"
|
|
51
|
+
>
|
|
52
|
+
<slot :item-click="handleItemClick" />
|
|
53
|
+
</div>
|
|
54
|
+
</transition>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style scoped lang="scss">
|
|
58
|
+
.dropup-bottom {
|
|
59
|
+
bottom: var(--button-height);
|
|
60
|
+
}
|
|
61
|
+
.dropdown-content-enter-active,
|
|
62
|
+
.dropdown-content-leave-active {
|
|
63
|
+
transition: all 0.3s;
|
|
64
|
+
}
|
|
65
|
+
.dropdown-content-enter-from,
|
|
66
|
+
.dropdown-content-leave-to {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transform: translateY(-10px);
|
|
69
|
+
}
|
|
70
|
+
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "main theme file for manguito components library. Other MCL components will need to import this file to run.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
"url": "https://github.com/bobbykim89/manguito-component-library.git"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
|
-
"gitHead": "
|
|
20
|
+
"gitHead": "79700f8b34204a6ecbe2f56d75f74a64c331055c"
|
|
21
21
|
}
|
package/theme/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export const colorType = [
|
|
|
21
21
|
'BEFOREBG',
|
|
22
22
|
'AFTERBG',
|
|
23
23
|
'HVBGCOLOR',
|
|
24
|
+
'FCBGCOLOR',
|
|
24
25
|
'ACTIVEBG',
|
|
25
26
|
'TEXTCOLOR',
|
|
26
27
|
'HVTEXTCOLOR',
|
|
@@ -119,6 +120,8 @@ class GenerateMCLClass extends MCLTheme {
|
|
|
119
120
|
return this.getBgColorClass()
|
|
120
121
|
case 'HVBGCOLOR':
|
|
121
122
|
return this.getHoverBgColorClass()
|
|
123
|
+
case 'FCBGCOLOR':
|
|
124
|
+
return this.getFocusBgColorClass()
|
|
122
125
|
case 'BEFOREBG':
|
|
123
126
|
return this.getBeforeBgColorClass()
|
|
124
127
|
case 'AFTERBG':
|
|
@@ -274,6 +277,12 @@ class GenerateMCLClass extends MCLTheme {
|
|
|
274
277
|
}
|
|
275
278
|
return MCLTheme.hoverBgColor[this.classValue as ColorPalette]
|
|
276
279
|
}
|
|
280
|
+
getFocusBgColorClass(): string {
|
|
281
|
+
if (!this.validateColorType) {
|
|
282
|
+
return ' '
|
|
283
|
+
}
|
|
284
|
+
return MCLTheme.focusBgColor[this.classValue as ColorPalette]
|
|
285
|
+
}
|
|
277
286
|
getBeforeBgColorClass(): string {
|
|
278
287
|
if (!this.validateColorType) {
|
|
279
288
|
return ' '
|
package/theme/theme.ts
CHANGED
|
@@ -60,7 +60,7 @@ abstract class MCLTheme {
|
|
|
60
60
|
white: 'bg-white',
|
|
61
61
|
transparent: 'bg-transparent',
|
|
62
62
|
}
|
|
63
|
-
// hover bg color class
|
|
63
|
+
// hover: bg color class
|
|
64
64
|
protected static hoverBgColor: ColorClass = {
|
|
65
65
|
primary: 'hover:bg-primary',
|
|
66
66
|
secondary: 'hover:bg-secondary',
|
|
@@ -80,6 +80,26 @@ abstract class MCLTheme {
|
|
|
80
80
|
white: 'hover:bg-white',
|
|
81
81
|
transparent: 'hover:bg-transparent',
|
|
82
82
|
}
|
|
83
|
+
// focus: bg color class
|
|
84
|
+
protected static focusBgColor: ColorClass = {
|
|
85
|
+
primary: 'focus:bg-primary',
|
|
86
|
+
secondary: 'focus:bg-secondary',
|
|
87
|
+
success: 'focus:bg-success',
|
|
88
|
+
danger: 'focus:bg-danger',
|
|
89
|
+
info: 'focus:bg-info',
|
|
90
|
+
warning: 'focus:bg-warning',
|
|
91
|
+
'light-1': 'focus:bg-light-1',
|
|
92
|
+
'light-2': 'focus:bg-light-2',
|
|
93
|
+
'light-3': 'focus:bg-light-3',
|
|
94
|
+
'light-4': 'focus:bg-light-4',
|
|
95
|
+
'dark-1': 'focus:bg-dark-1',
|
|
96
|
+
'dark-2': 'focus:bg-dark-2',
|
|
97
|
+
'dark-3': 'focus:bg-dark-3',
|
|
98
|
+
'dark-4': 'focus:bg-dark-4',
|
|
99
|
+
black: 'focus:bg-black',
|
|
100
|
+
white: 'focus:bg-white',
|
|
101
|
+
transparent: 'focus:bg-transparent',
|
|
102
|
+
}
|
|
83
103
|
// ::before bg color class
|
|
84
104
|
protected static beforeBgColor: ColorClass = {
|
|
85
105
|
primary: 'before:bg-primary',
|
package/themeVariables.cjs
CHANGED
|
@@ -83,6 +83,11 @@ const mclTheme = plugin.withOptions(
|
|
|
83
83
|
'@apply ring-0': {},
|
|
84
84
|
},
|
|
85
85
|
},
|
|
86
|
+
[`.btn.btn-${e(key)}.btn-no-ring`]: {
|
|
87
|
+
'&:focus': {
|
|
88
|
+
'@apply ring-0': {},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
86
91
|
[`.btn.btn-progress.btn-${e(key)}`]: {
|
|
87
92
|
[`@apply before:bg-${key} border-${key} text-${key} bg-transparent`]:
|
|
88
93
|
{},
|
|
@@ -364,6 +369,12 @@ const mclTheme = plugin.withOptions(
|
|
|
364
369
|
'@apply ring-0': {},
|
|
365
370
|
},
|
|
366
371
|
},
|
|
372
|
+
'.btn.btn-no-ring': {
|
|
373
|
+
'@apply ring-offset-0': {},
|
|
374
|
+
'&:focus': {
|
|
375
|
+
'@apply ring-0': {},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
367
378
|
'.btn.btn-invert': {
|
|
368
379
|
'@apply border-2 border-primary text-primary bg-white': {},
|
|
369
380
|
'&:hover': {
|