@bobbykim/manguito-theme 0.2.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/README.md CHANGED
@@ -92,7 +92,7 @@ import type { <Type you need> } from '@bobbykim/manguito-theme/theme/theme.types
92
92
  <template>...</template>
93
93
  ```
94
94
 
95
- ## Authors
95
+ ## Maintainers
96
96
 
97
97
  - **Bobby Kim** - _Initial work_
98
98
 
package/index.ts CHANGED
@@ -1,10 +1,35 @@
1
- import GenerateMCLClass from './theme'
2
- import type { ClassType, InputType } from './theme'
1
+ import GenerateMCLClass, {
2
+ colorType,
3
+ textType,
4
+ borderWidthType,
5
+ scaleType,
6
+ } from './theme'
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'
3
13
 
4
14
  const generateClass = (type: ClassType, value: InputType): string => {
5
15
  const mclGenerate = new GenerateMCLClass(type, value)
6
-
7
- return mclGenerate.generateClassName()
16
+ if ((<string[]>(<unknown>colorType)).includes(type)) {
17
+ return mclGenerate.generateColorClass()
18
+ }
19
+ if ((<string[]>(<unknown>textType)).includes(type)) {
20
+ return mclGenerate.generateTextClass()
21
+ }
22
+ if ((<string[]>(<unknown>borderWidthType)).includes(type)) {
23
+ return mclGenerate.generateBorderWidthClass()
24
+ }
25
+ if ((<string[]>(<unknown>scaleType)).includes(type)) {
26
+ return mclGenerate.generateScaleClass()
27
+ }
28
+ // return spacing type if all of above is false
29
+ return mclGenerate.generateSpacingClass()
8
30
  }
9
31
 
10
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,3 @@
1
+ export interface CollapseEvent extends Event {
2
+ visible: boolean
3
+ }
@@ -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>
@@ -0,0 +1,4 @@
1
+ export type InjectType = {
2
+ active: boolean
3
+ buttonHeight: number
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobbykim/manguito-theme",
3
- "version": "0.2.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": "aaee823ffe3d684532eb08f0553b96a4a5475a88"
20
+ "gitHead": "79700f8b34204a6ecbe2f56d75f74a64c331055c"
21
21
  }
package/theme/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import MCLTheme from './theme'
1
+ import MCLTheme from './theme.js'
2
2
  import type {
3
3
  ColorPalette,
4
4
  HeadingSize,
@@ -16,68 +16,83 @@ import type {
16
16
  * @Export GenerateMCLClass - extends MCLTheme class
17
17
  */
18
18
 
19
- export type ClassType =
20
- // color class
21
- | 'BGCOLOR'
22
- | 'BEFOREBG'
23
- | 'AFTERBG'
24
- | 'HVBGCOLOR'
25
- | 'ACTIVEBG'
26
- | 'TEXTCOLOR'
27
- | 'HVTEXTCOLOR'
28
- | 'DISABLEDTEXTCOLOR'
29
- | 'SVGFILL'
30
- | 'RINGCOLOR'
31
- | 'FOCUSRING'
32
- | 'ACTIVERING'
33
- | 'BTNCOLOR'
34
- | 'LISTCOLOR'
35
- // header text class
36
- | 'H1'
37
- | 'H2'
38
- | 'H3'
39
- | 'H4'
40
- // body text class
41
- | 'BODYTEXT'
42
- // font weight class
43
- | 'FONTWEIGHT'
44
- // border color class
45
- | 'BORDER'
46
- | 'BORDERX'
47
- | 'BORDERY'
48
- | 'BORDERT'
49
- | 'BORDERB'
50
- | 'BORDERL'
51
- | 'BORDERR'
52
- // border width class
53
- | 'BORDERW'
54
- | 'BORDERXW'
55
- | 'BORDERYW'
56
- | 'BORDERTW'
57
- | 'BORDERBW'
58
- | 'BORDERLW'
59
- | 'BORDERRW'
60
- // opacity class
61
- | 'BGOPACITY'
62
- | 'OPACITY'
63
- // spacing class
64
- | 'MARGIN'
65
- | 'MARGINX'
66
- | 'MARGINY'
67
- | 'MARGINT'
68
- | 'MARGINB'
69
- | 'MARGINL'
70
- | 'MARGINR'
71
- | 'PADDING'
72
- | 'PADDINGX'
73
- | 'PADDINGY'
74
- | 'PADDINGT'
75
- | 'PADDINGB'
76
- | 'PADDINGL'
77
- | 'PADDINGR'
78
- | 'GAP'
79
- // text align class
80
- | 'TEXTALIGN'
19
+ export const colorType = [
20
+ 'BGCOLOR',
21
+ 'BEFOREBG',
22
+ 'AFTERBG',
23
+ 'HVBGCOLOR',
24
+ 'FCBGCOLOR',
25
+ 'ACTIVEBG',
26
+ 'TEXTCOLOR',
27
+ 'HVTEXTCOLOR',
28
+ 'DISABLEDTEXTCOLOR',
29
+ 'SVGFILL',
30
+ 'RINGCOLOR',
31
+ 'FOCUSRING',
32
+ 'ACTIVERING',
33
+ 'OFFSETRING',
34
+ 'DUMMYCOLOR',
35
+ 'BTNCOLOR',
36
+ 'LISTCOLOR',
37
+ 'BORDER',
38
+ 'BORDERX',
39
+ 'BORDERY',
40
+ 'BORDERT',
41
+ 'BORDERB',
42
+ 'BORDERL',
43
+ 'BORDERR',
44
+ ] as const
45
+
46
+ export const textType = [
47
+ 'H1',
48
+ 'H2',
49
+ 'H3',
50
+ 'H4',
51
+ 'BODYTEXT',
52
+ 'FONTWEIGHT',
53
+ 'TEXTALIGN',
54
+ ] as const
55
+ export const borderWidthType = [
56
+ 'BORDERW',
57
+ 'BORDERXW',
58
+ 'BORDERYW',
59
+ 'BORDERTW',
60
+ 'BORDERBW',
61
+ 'BORDERLW',
62
+ 'BORDERRW',
63
+ ] as const
64
+ export const scaleType = ['BGOPACITY', 'OPACITY'] as const
65
+ export const spacingType = [
66
+ 'MARGIN',
67
+ 'MARGINX',
68
+ 'MARGINY',
69
+ 'MARGINT',
70
+ 'MARGINB',
71
+ 'MARGINL',
72
+ 'MARGINR',
73
+ 'PADDING',
74
+ 'PADDINGX',
75
+ 'PADDINGY',
76
+ 'PADDINGT',
77
+ 'PADDINGB',
78
+ 'PADDINGL',
79
+ 'PADDINGR',
80
+ 'GAP',
81
+ ] as const
82
+ const allType = [
83
+ ...colorType,
84
+ ...textType,
85
+ ...borderWidthType,
86
+ ...scaleType,
87
+ ...spacingType,
88
+ ] as const
89
+
90
+ export type ClassType = (typeof allType)[number]
91
+ export type ColorClassType = (typeof colorType)[number]
92
+ export type TextClassType = (typeof textType)[number]
93
+ export type BorderWidthClassType = (typeof borderWidthType)[number]
94
+ export type ScaleClassType = (typeof scaleType)[number]
95
+ export type SpacingClassType = (typeof spacingType)[number]
81
96
 
82
97
  export type InputType =
83
98
  | ColorPalette
@@ -98,13 +113,15 @@ class GenerateMCLClass extends MCLTheme {
98
113
  this.classType = classType
99
114
  this.classValue = classValue
100
115
  }
101
- generateClassName(): string {
116
+ // handle color classes types
117
+ generateColorClass(): string {
102
118
  switch (this.classType) {
103
- // case: color class
104
119
  case 'BGCOLOR':
105
120
  return this.getBgColorClass()
106
121
  case 'HVBGCOLOR':
107
122
  return this.getHoverBgColorClass()
123
+ case 'FCBGCOLOR':
124
+ return this.getFocusBgColorClass()
108
125
  case 'BEFOREBG':
109
126
  return this.getBeforeBgColorClass()
110
127
  case 'AFTERBG':
@@ -125,11 +142,12 @@ class GenerateMCLClass extends MCLTheme {
125
142
  return this.getFocusRingColorClass()
126
143
  case 'ACTIVERING':
127
144
  return this.getActiveRingColorClass()
145
+ case 'OFFSETRING':
146
+ return this.getRingOffsetColorClass()
128
147
  case 'BTNCOLOR':
129
148
  return this.getBtnColorClass()
130
149
  case 'LISTCOLOR':
131
150
  return this.getRingColorClass()
132
- // case: border color class
133
151
  case 'BORDER':
134
152
  return this.getBorderColorClass()
135
153
  case 'BORDERX':
@@ -144,6 +162,13 @@ class GenerateMCLClass extends MCLTheme {
144
162
  return this.getBorderLeftColorClass()
145
163
  case 'BORDERR':
146
164
  return this.getBorderRightColorClass()
165
+ default:
166
+ return ' '
167
+ }
168
+ }
169
+ // handle text class types
170
+ generateTextClass(): string {
171
+ switch (this.classType) {
147
172
  // case: heading text class
148
173
  case 'H1':
149
174
  return this.getH1Class()
@@ -162,6 +187,13 @@ class GenerateMCLClass extends MCLTheme {
162
187
  // case: text alignment class
163
188
  case 'TEXTALIGN':
164
189
  return this.getTextAlignClass()
190
+ default:
191
+ return ' '
192
+ }
193
+ }
194
+ // handle border width class types
195
+ generateBorderWidthClass(): string {
196
+ switch (this.classType) {
165
197
  // case: border width class
166
198
  case 'BORDERW':
167
199
  return this.getBorderWidthClass()
@@ -177,13 +209,24 @@ class GenerateMCLClass extends MCLTheme {
177
209
  return this.getBorderLeftWidthClass()
178
210
  case 'BORDERRW':
179
211
  return this.getBorderWidthClass()
212
+ default:
213
+ return ' '
214
+ }
215
+ }
216
+ generateScaleClass(): string {
217
+ switch (this.classType) {
180
218
  // case: opacity class
181
219
  case 'OPACITY':
182
220
  return this.getOpacityClass()
183
221
  case 'BGOPACITY':
184
222
  return this.getBgOpacityClass()
185
- // case: spacing class
186
- // margins
223
+ default:
224
+ return ' '
225
+ }
226
+ }
227
+ generateSpacingClass(): string {
228
+ switch (this.classType) {
229
+ // case: margins
187
230
  case 'MARGIN':
188
231
  return this.getMarginClass()
189
232
  case 'MARGINX':
@@ -198,7 +241,7 @@ class GenerateMCLClass extends MCLTheme {
198
241
  return this.getMarginLeftClass()
199
242
  case 'MARGINR':
200
243
  return this.getMarginRightClass()
201
- // paddings
244
+ // case: paddings
202
245
  case 'PADDING':
203
246
  return this.getPaddingClass()
204
247
  case 'PADDINGX':
@@ -213,7 +256,7 @@ class GenerateMCLClass extends MCLTheme {
213
256
  return this.getPaddingLeftClass()
214
257
  case 'PADDINGR':
215
258
  return this.getPaddingRightClass()
216
- // gap
259
+ // case: gap
217
260
  case 'GAP':
218
261
  return this.getGapClass()
219
262
  default:
@@ -234,6 +277,12 @@ class GenerateMCLClass extends MCLTheme {
234
277
  }
235
278
  return MCLTheme.hoverBgColor[this.classValue as ColorPalette]
236
279
  }
280
+ getFocusBgColorClass(): string {
281
+ if (!this.validateColorType) {
282
+ return ' '
283
+ }
284
+ return MCLTheme.focusBgColor[this.classValue as ColorPalette]
285
+ }
237
286
  getBeforeBgColorClass(): string {
238
287
  if (!this.validateColorType) {
239
288
  return ' '
@@ -297,6 +346,12 @@ class GenerateMCLClass extends MCLTheme {
297
346
  }
298
347
  return MCLTheme.ringActiveColor[this.classValue as ColorPalette]
299
348
  }
349
+ getRingOffsetColorClass(): string {
350
+ if (!this.validateColorType) {
351
+ return ' '
352
+ }
353
+ return MCLTheme.ringOffsetColor[this.classValue as ColorPalette]
354
+ }
300
355
  // btn & list
301
356
  getBtnColorClass(): string {
302
357
  if (!this.validateColorType) {
@@ -604,6 +659,9 @@ class GenerateMCLClass extends MCLTheme {
604
659
  }
605
660
  return validatorRe.test(this.classValue)
606
661
  }
662
+ dummyMethod(): any {
663
+ return this.getRingOffsetColorClass()
664
+ }
607
665
  }
608
666
 
609
667
  export default GenerateMCLClass
package/theme/theme.ts CHANGED
@@ -3,93 +3,40 @@
3
3
  * @Interface - Classes for color, header text, body text, border color, border width(top, left).
4
4
  * @Export RDSTheme - an abstract class
5
5
  */
6
+ import type {
7
+ ColorPalette,
8
+ HeadingSize,
9
+ BodyText,
10
+ FontWeight,
11
+ Range,
12
+ OpacityRange,
13
+ SpacingLevel,
14
+ Position,
15
+ } from './theme.types'
6
16
 
7
- export interface ColorClass {
8
- primary: string
9
- secondary: string
10
- success: string
11
- info: string
12
- warning: string
13
- danger: string
14
- 'light-1': string
15
- 'light-2': string
16
- 'light-3': string
17
- 'light-4': string
18
- 'dark-1': string
19
- 'dark-2': string
20
- 'dark-3': string
21
- 'dark-4': string
22
- black: string
23
- white: string
24
- transparent: string
17
+ export type ColorClass = {
18
+ [key in ColorPalette]: string
25
19
  }
26
-
27
- export interface HeaderSizeClass {
28
- sm: string
29
- md: string
30
- lg: string
31
- xl: string
20
+ export type HeaderSizeClass = {
21
+ [key in HeadingSize]: string
32
22
  }
33
-
34
- export interface BodyTextClass {
35
- xs: string
36
- sm: string
37
- md: string
38
- lg: string
39
- xl: string
23
+ export type BodyTextClass = {
24
+ [key in BodyText]: string
40
25
  }
41
-
42
- export interface FontWeightClass {
43
- light: string
44
- normal: string
45
- bold: string
26
+ export type FontWeightClass = {
27
+ [key in FontWeight]: string
46
28
  }
47
-
48
- export interface BorderWidth {
49
- 1: string
50
- 2: string
51
- 3: string
52
- 4: string
53
- 5: string
54
- 6: string
55
- 7: string
56
- 8: string
57
- 9: string
58
- 10: string
59
- 11: string
60
- 12: string
29
+ export type BorderWidth = {
30
+ [key in Range<1, 12>]: string
61
31
  }
62
-
63
- export interface Opacity {
64
- 10: string
65
- 20: string
66
- 30: string
67
- 40: string
68
- 50: string
69
- 60: string
70
- 70: string
71
- 80: string
72
- 90: string
73
- 100: string
32
+ export type Opacity = {
33
+ [key in OpacityRange]: string
74
34
  }
75
-
76
- export interface Spacing {
77
- '0': string
78
- '3xs': string
79
- '2xs': string
80
- xs: string
81
- sm: string
82
- md: string
83
- lg: string
84
- xl: string
85
- '2xl': string
86
- '3xl': string
35
+ export type Spacing = {
36
+ [key in SpacingLevel]: string
87
37
  }
88
-
89
- export interface Alignment {
90
- left: string
91
- center: string
92
- right: string
38
+ export type Alignment = {
39
+ [key in Position]: string
93
40
  }
94
41
 
95
42
  abstract class MCLTheme {
@@ -113,7 +60,7 @@ abstract class MCLTheme {
113
60
  white: 'bg-white',
114
61
  transparent: 'bg-transparent',
115
62
  }
116
- // hover bg color class
63
+ // hover: bg color class
117
64
  protected static hoverBgColor: ColorClass = {
118
65
  primary: 'hover:bg-primary',
119
66
  secondary: 'hover:bg-secondary',
@@ -133,6 +80,26 @@ abstract class MCLTheme {
133
80
  white: 'hover:bg-white',
134
81
  transparent: 'hover:bg-transparent',
135
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
+ }
136
103
  // ::before bg color class
137
104
  protected static beforeBgColor: ColorClass = {
138
105
  primary: 'before:bg-primary',
@@ -333,6 +300,45 @@ abstract class MCLTheme {
333
300
  white: 'active:ring-white',
334
301
  transparent: 'active:ring-transparent',
335
302
  }
303
+ // ring-offset color class
304
+ protected static ringOffsetColor: ColorClass = {
305
+ primary: 'ring-offset-primary',
306
+ secondary: 'ring-offset-secondary',
307
+ success: 'ring-offset-success',
308
+ danger: 'ring-offset-danger',
309
+ info: 'ring-offset-info',
310
+ warning: 'ring-offset-warning',
311
+ 'light-1': 'ring-offset-light-1',
312
+ 'light-2': 'ring-offset-light-2',
313
+ 'light-3': 'ring-offset-light-3',
314
+ 'light-4': 'ring-offset-light-4',
315
+ 'dark-1': 'ring-offset-dark-1',
316
+ 'dark-2': 'ring-offset-dark-2',
317
+ 'dark-3': 'ring-offset-dark-3',
318
+ 'dark-4': 'ring-offset-dark-4',
319
+ black: 'ring-offset-black',
320
+ white: 'ring-offset-white',
321
+ transparent: 'ring-offset-transparent',
322
+ }
323
+ protected static dummyColor: ColorClass = {
324
+ primary: 'ring-offset-primary',
325
+ secondary: 'ring-offset-secondary',
326
+ success: 'ring-offset-success',
327
+ danger: 'ring-offset-danger',
328
+ info: 'ring-offset-info',
329
+ warning: 'ring-offset-warning',
330
+ 'light-1': 'ring-offset-light-1',
331
+ 'light-2': 'ring-offset-light-2',
332
+ 'light-3': 'ring-offset-light-3',
333
+ 'light-4': 'ring-offset-light-4',
334
+ 'dark-1': 'ring-offset-dark-1',
335
+ 'dark-2': 'ring-offset-dark-2',
336
+ 'dark-3': 'ring-offset-dark-3',
337
+ 'dark-4': 'ring-offset-dark-4',
338
+ black: 'ring-offset-black',
339
+ white: 'ring-offset-white',
340
+ transparent: 'ring-offset-transparent',
341
+ }
336
342
  // btn color class
337
343
  protected static btnColor: ColorClass = {
338
344
  primary: 'btn-primary',
@@ -57,6 +57,7 @@ export type Range<T extends number, R extends number> = IntRange<
57
57
  _RangeArray<T>,
58
58
  R
59
59
  >
60
+
60
61
  export type ButtonSize = 'small' | 'medium' | 'large'
61
62
  export type OpacityRange = 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100
62
63
  export type Directions = 'left' | 'right'
@@ -4,7 +4,6 @@ const mclTheme = plugin.withOptions(
4
4
  () => {
5
5
  return ({ addBase, theme, addComponents, addUtilities, e }) => {
6
6
  const colors = theme('colors')
7
- // console.log(colors)
8
7
  const listColors = Object.keys(colors).reduce((accumulator, key) => {
9
8
  if (typeof colors[key] === 'string') {
10
9
  return {
@@ -29,6 +28,34 @@ const mclTheme = plugin.withOptions(
29
28
  ),
30
29
  }
31
30
  }, {})
31
+ const linkColors = Object.keys(colors).reduce((accumulator, key) => {
32
+ const invalidKeys = ['current', 'transparent', 'inherit']
33
+ if (invalidKeys.includes(key)) {
34
+ return
35
+ }
36
+ if (typeof colors[key] === 'string') {
37
+ return {
38
+ ...accumulator,
39
+ [`.mcl-link.text-${e(key)}`]: {
40
+ '&:hover': {
41
+ [`@apply text-${key}/60`]: {},
42
+ },
43
+ },
44
+ }
45
+ }
46
+ const colorShades = Object.keys(colors[key])
47
+ return {
48
+ ...accumulator,
49
+ ...colorShades.reduce((acc, level) => ({
50
+ ...acc,
51
+ [`.mcl-link.text-${e(key)}-${level}`]: {
52
+ '&:hover': {
53
+ [`@apply text-${key}-${level}/60`]: {},
54
+ },
55
+ },
56
+ })),
57
+ }
58
+ })
32
59
  const btnColors = Object.keys(colors).reduce((accumulator, key) => {
33
60
  if (typeof colors[key] === 'string') {
34
61
  return {
@@ -56,6 +83,18 @@ const mclTheme = plugin.withOptions(
56
83
  '@apply ring-0': {},
57
84
  },
58
85
  },
86
+ [`.btn.btn-${e(key)}.btn-no-ring`]: {
87
+ '&:focus': {
88
+ '@apply ring-0': {},
89
+ },
90
+ },
91
+ [`.btn.btn-progress.btn-${e(key)}`]: {
92
+ [`@apply before:bg-${key} border-${key} text-${key} bg-transparent`]:
93
+ {},
94
+ '&:hover, &:focus': {
95
+ '@apply text-white': {},
96
+ },
97
+ },
59
98
  }
60
99
  }
61
100
 
@@ -90,6 +129,13 @@ const mclTheme = plugin.withOptions(
90
129
  '@apply ring-0': {},
91
130
  },
92
131
  },
132
+ [`.btn.btn-progress.btn-${e(key)}-${level}`]: {
133
+ [`@apply before:bg-${key}-${level} border-${key}-${level} text-${key}-${level} bg-transparent`]:
134
+ {},
135
+ '&:hover, &:focus': {
136
+ '@apply text-white': {},
137
+ },
138
+ },
93
139
  }),
94
140
  {}
95
141
  ),
@@ -252,6 +298,10 @@ const mclTheme = plugin.withOptions(
252
298
  '.mcl-link': {
253
299
  color: theme('colors.primary'),
254
300
  textDecoration: 'underline',
301
+ '@apply transition-colors duration-200 ease-in': {},
302
+ '&:hover': {
303
+ '@apply text-primary/60': {},
304
+ },
255
305
  },
256
306
  '.mcl-list': {
257
307
  'list-style-position': 'inside',
@@ -319,16 +369,59 @@ const mclTheme = plugin.withOptions(
319
369
  '@apply ring-0': {},
320
370
  },
321
371
  },
372
+ '.btn.btn-no-ring': {
373
+ '@apply ring-offset-0': {},
374
+ '&:focus': {
375
+ '@apply ring-0': {},
376
+ },
377
+ },
322
378
  '.btn.btn-invert': {
323
379
  '@apply border-2 border-primary text-primary bg-white': {},
324
380
  '&:hover': {
325
381
  '@apply bg-primary text-white bg-opacity-100': {},
326
382
  },
327
383
  },
384
+ '.btn.btn-progress': {
385
+ position: 'relative',
386
+ '&::before': {
387
+ content: '""',
388
+ position: 'absolute',
389
+ height: '100%',
390
+ width: '0',
391
+ bottom: '0',
392
+ left: '0',
393
+ transition: 'width 300ms linear',
394
+ },
395
+ '@apply before:bg-primary border-2 border-primary text-primary bg-transparent':
396
+ {},
397
+ },
398
+ '.btn.btn-progress > *': {
399
+ position: 'relative',
400
+ },
401
+ '.btn.btn-progress.btn-progress-r': {
402
+ '&::before': {
403
+ left: '100%',
404
+ transition: 'all 300ms linear',
405
+ },
406
+ },
407
+ '.btn.btn-progress:hover, .btn.btn-progress:focus': {
408
+ '@apply text-white': {},
409
+ '&::before': {
410
+ width: '100%',
411
+ },
412
+ },
413
+ '.btn.btn-progress.btn-progress-r:hover, .btn.btn-progress.btn-progress-r:focus':
414
+ {
415
+ '&::before': {
416
+ width: '100%',
417
+ left: '0',
418
+ },
419
+ },
328
420
  })
329
421
 
330
422
  addUtilities(listColors)
331
423
  addUtilities(btnColors)
424
+ addUtilities(linkColors)
332
425
  }
333
426
  },
334
427
  (options = {}) => {