@liftkit-vue/core 0.1.0 → 0.2.1

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +11 -7
  3. package/src/components/LkBadge.vue +64 -0
  4. package/src/components/LkButton.vue +102 -0
  5. package/src/components/LkCard.vue +85 -0
  6. package/src/components/LkColumn.vue +40 -0
  7. package/src/components/LkContainer.vue +26 -0
  8. package/src/components/LkDropdown/LkDropdown.vue +14 -0
  9. package/src/components/LkDropdown/LkDropdownMenu.vue +99 -0
  10. package/src/components/LkDropdown/LkDropdownTrigger.vue +39 -0
  11. package/src/components/LkDropdown/index.ts +3 -0
  12. package/src/components/LkGrid.vue +58 -0
  13. package/src/components/LkHeading.vue +43 -0
  14. package/src/components/LkIcon.vue +56 -0
  15. package/src/components/LkIconButton.vue +78 -0
  16. package/src/components/LkImage.vue +62 -0
  17. package/src/components/LkMaterialLayer.vue +147 -0
  18. package/src/components/LkMenuItem.vue +51 -0
  19. package/src/components/LkNavbar.vue +113 -0
  20. package/src/components/LkPlaceholderBlock.vue +9 -0
  21. package/src/components/LkRow.vue +40 -0
  22. package/src/components/LkSection.vue +45 -0
  23. package/src/components/LkSelect.vue +167 -0
  24. package/src/components/LkSelectMenu.vue +67 -0
  25. package/src/components/LkSelectOption.vue +56 -0
  26. package/src/components/LkSelectTrigger.vue +43 -0
  27. package/src/components/LkSnackbar.vue +127 -0
  28. package/src/components/LkStateLayer.vue +33 -0
  29. package/src/components/LkSticker.vue +42 -0
  30. package/src/components/LkSwitch.vue +90 -0
  31. package/src/components/LkTabContent.vue +33 -0
  32. package/src/components/LkTabLink.vue +50 -0
  33. package/src/components/LkTabMenu.vue +59 -0
  34. package/src/components/LkTabs.vue +65 -0
  35. package/src/components/LkText.vue +37 -0
  36. package/src/components/LkTextInput.vue +105 -0
  37. package/src/components/LkTheme.vue +49 -0
  38. package/src/components/LkThemeController.vue +396 -0
  39. package/src/css/index.css +3 -1
  40. package/src/css/liftkit-core.css +5 -464
  41. package/src/css/liftkit-reset.css +74 -0
  42. package/src/css/liftkit-tokens.css +375 -0
@@ -0,0 +1,78 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+ import { getOnToken } from '../utils/colorUtils'
5
+ import LkIcon from './LkIcon.vue'
6
+ import LkStateLayer from './LkStateLayer.vue'
7
+
8
+ export interface LkIconButtonProps {
9
+ icon?: string
10
+ variant?: 'fill' | 'outline' | 'text'
11
+ color?: LkColor
12
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
13
+ fontClass?: LkFontClass
14
+ }
15
+
16
+ const props = withDefaults(defineProps<LkIconButtonProps>(), {
17
+ icon: 'roller-coaster',
18
+ variant: 'fill',
19
+ color: 'primary',
20
+ size: 'md',
21
+ fontClass: 'body',
22
+ })
23
+
24
+ defineOptions({
25
+ inheritAttrs: false,
26
+ })
27
+
28
+ const dataAttrs = computed(() =>
29
+ propsToDataAttrs(
30
+ { variant: props.variant, color: props.color, size: props.size },
31
+ 'icon-button',
32
+ ),
33
+ )
34
+
35
+ const onToken = computed(() => getOnToken(props.color) as LkColor)
36
+
37
+ const iconStrokeWidth = computed(() => {
38
+ switch (props.fontClass) {
39
+ case 'display1':
40
+ case 'display2':
41
+ case 'title1':
42
+ return 1.5
43
+ case 'subheading':
44
+ case 'label':
45
+ case 'caption':
46
+ case 'capline':
47
+ return 2
48
+ default:
49
+ return 1.75
50
+ }
51
+ })
52
+
53
+ const iconColor = computed(() => {
54
+ switch (props.variant) {
55
+ case 'outline':
56
+ case 'text':
57
+ return props.color
58
+ case 'fill':
59
+ return onToken.value
60
+ default:
61
+ return onToken.value
62
+ }
63
+ })
64
+ </script>
65
+
66
+ <template>
67
+ <button
68
+ data-lk-component="icon-button"
69
+ type="button"
70
+ v-bind="{ ...$attrs, ...dataAttrs }"
71
+ :class="`${fontClass}`"
72
+ >
73
+ <div>
74
+ <LkIcon :name="icon" :color="iconColor" :stroke-width="iconStrokeWidth" />
75
+ </div>
76
+ <LkStateLayer :bg-color="iconColor" />
77
+ </button>
78
+ </template>
@@ -0,0 +1,62 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+
5
+ type LkAspectRatio =
6
+ | 'auto'
7
+ | '1/1'
8
+ | '2.39/1'
9
+ | '2/1'
10
+ | '16/9'
11
+ | '3/2'
12
+ | '4/3'
13
+ | '5/4'
14
+ | '1/2.39'
15
+ | '1/2'
16
+ | '9/16'
17
+ | '4/5'
18
+
19
+ interface LkImageProps {
20
+ aspect?: LkAspectRatio
21
+ borderRadius?: LkSizeUnit | 'none' | 'zero'
22
+ objectFit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
23
+ width?: LkSizeUnit | 'auto'
24
+ height?: LkSizeUnit | 'auto'
25
+ src?: string
26
+ alt?: string
27
+ }
28
+
29
+ const props = withDefaults(defineProps<LkImageProps>(), {
30
+ aspect: 'auto',
31
+ objectFit: 'fill',
32
+ width: 'auto',
33
+ height: 'auto',
34
+ alt: '',
35
+ })
36
+
37
+ defineOptions({
38
+ inheritAttrs: false,
39
+ })
40
+
41
+ const dataAttrs = computed(() =>
42
+ propsToDataAttrs(
43
+ {
44
+ aspect: props.aspect,
45
+ borderRadius: props.borderRadius,
46
+ objectFit: props.objectFit,
47
+ width: props.width,
48
+ height: props.height,
49
+ },
50
+ 'image',
51
+ ),
52
+ )
53
+ </script>
54
+
55
+ <template>
56
+ <img
57
+ data-lk-component="image"
58
+ v-bind="{ ...$attrs, ...dataAttrs }"
59
+ :src="props.src"
60
+ :alt="props.alt"
61
+ />
62
+ </template>
@@ -0,0 +1,147 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+
5
+ type LkMatProps_Glass = {
6
+ thickness?: 'thick' | 'normal' | 'thin'
7
+ tint?: LkColor
8
+ tintOpacity?: number
9
+ light?: boolean
10
+ lightExpression?: string
11
+ }
12
+
13
+ type LkMatProps_Flat = {
14
+ bgColor?: LkColorWithOnToken
15
+ textColor?: LkColor
16
+ }
17
+
18
+ type LkMaterialType = 'flat' | 'glass' | 'debug'
19
+
20
+ interface LkMaterialLayerProps {
21
+ zIndex?: number
22
+ type?: LkMaterialType
23
+ materialProps?: LkMatProps_Glass | LkMatProps_Flat
24
+ }
25
+
26
+ const props = withDefaults(defineProps<LkMaterialLayerProps>(), {
27
+ zIndex: 0,
28
+ })
29
+
30
+ defineOptions({
31
+ inheritAttrs: false,
32
+ })
33
+
34
+ const matDataAttrs = computed(() => {
35
+ if (props.materialProps && props.type) {
36
+ return propsToDataAttrs(props.materialProps as Record<string, unknown>, props.type)
37
+ }
38
+ return {}
39
+ })
40
+
41
+ // Glass-specific computed styles
42
+ const glassProps = computed(() => props.materialProps as LkMatProps_Glass | undefined)
43
+
44
+ const tintStyle = computed(() => ({
45
+ opacity: glassProps.value?.tintOpacity || 0.2,
46
+ backgroundColor: `var(--lk-${glassProps.value?.tint || 'transparent'})`,
47
+ }))
48
+
49
+ const textureStyle = computed(() => {
50
+ const thickness = glassProps.value?.thickness || 'normal'
51
+ return {
52
+ '--blur-thick': 'var(--lk-size-lg)',
53
+ '--blur-normal': 'var(--lk-size-md)',
54
+ '--blur-thin': 'var(--lk-size-xs)',
55
+ zIndex: 1,
56
+ isolation: 'isolate',
57
+ backdropFilter: `blur(var(--blur-${thickness}))`,
58
+ } as Record<string, string | number>
59
+ })
60
+
61
+ const lightStyle = computed(() => ({
62
+ background: glassProps.value?.lightExpression || 'none',
63
+ mixBlendMode: 'soft-light' as const,
64
+ opacity: 1,
65
+ }))
66
+
67
+ const glassFillOpacity = computed(() => {
68
+ const thickness = glassProps.value?.thickness || 'normal'
69
+ switch (thickness) {
70
+ case 'thick': return 0.8
71
+ case 'normal': return 0.6
72
+ case 'thin': return 0.4
73
+ default: return 0.6
74
+ }
75
+ })
76
+
77
+ const glassFillStyle = computed(() => ({
78
+ backgroundColor: 'var(--lk-surface)',
79
+ opacity: glassFillOpacity.value,
80
+ }))
81
+
82
+ // Flat-specific
83
+ const flatProps = computed(() => props.materialProps as LkMatProps_Flat | undefined)
84
+
85
+ const flatBgStyle = computed(() => ({
86
+ backgroundColor: flatProps.value?.bgColor
87
+ ? `var(--lk-${flatProps.value.bgColor})`
88
+ : 'var(--lk-surface)',
89
+ }))
90
+ </script>
91
+
92
+ <template>
93
+ <div
94
+ data-lk-component="material-layer"
95
+ :data-lk-material-type="props.type"
96
+ :style="{ zIndex: props.zIndex }"
97
+ >
98
+ <!-- Glass material -->
99
+ <template v-if="props.type === 'glass'">
100
+ <div>
101
+ <div data-lk-material-sublayer="texture" :style="textureStyle">
102
+ <div
103
+ v-if="glassProps?.tint"
104
+ data-lk-material-sublayer="tint"
105
+ :style="tintStyle"
106
+ >
107
+ <div
108
+ v-if="glassProps?.light"
109
+ data-lk-material-sublayer="light"
110
+ :style="lightStyle"
111
+ ></div>
112
+ </div>
113
+ </div>
114
+ <div
115
+ data-lk-material-sublayer="base-glass-fill"
116
+ :style="glassFillStyle"
117
+ ></div>
118
+ </div>
119
+ </template>
120
+
121
+ <!-- Flat material -->
122
+ <template v-if="props.type === 'flat'">
123
+ <div>
124
+ <div
125
+ data-lk-material-sublayer="bgColor"
126
+ :style="flatBgStyle"
127
+ ></div>
128
+ </div>
129
+ </template>
130
+ </div>
131
+ </template>
132
+
133
+ <style scoped>
134
+ [data-lk-component="material-layer"] {
135
+ position: absolute;
136
+ inset: 0;
137
+ width: 100%;
138
+ height: 100%;
139
+ pointer-events: none;
140
+ }
141
+
142
+ [data-lk-component="material-layer"] [data-lk-material-sublayer] {
143
+ position: absolute;
144
+ inset: 0;
145
+ pointer-events: none;
146
+ }
147
+ </style>
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ import LkStateLayer from './LkStateLayer.vue'
3
+ import LkIcon from './LkIcon.vue'
4
+
5
+ export interface LkIconProps {
6
+ name?: string
7
+ color?: LkColor
8
+ fontClass?: LkFontClass
9
+ strokeWidth?: number
10
+ opticShift?: boolean
11
+ [key: string]: unknown
12
+ }
13
+
14
+ export interface LkMenuItemProps {
15
+ startIcon?: LkIconProps
16
+ endIcon?: LkIconProps
17
+ fontClass?: LkFontClass
18
+ title?: string
19
+ }
20
+
21
+ const props = withDefaults(defineProps<LkMenuItemProps>(), {
22
+ fontClass: 'body',
23
+ })
24
+
25
+ defineOptions({
26
+ inheritAttrs: false,
27
+ })
28
+ </script>
29
+
30
+ <template>
31
+ <div
32
+ data-lk-component="menu-item"
33
+ :title="title"
34
+ v-bind="$attrs"
35
+ >
36
+ <LkIcon
37
+ v-if="startIcon"
38
+ v-bind="startIcon"
39
+ data-lk-icon-position="start"
40
+ />
41
+ <p data-lk-menu-item-element="content-wrap">
42
+ <slot />
43
+ </p>
44
+ <LkIcon
45
+ v-if="endIcon"
46
+ v-bind="endIcon"
47
+ data-lk-icon-position="end"
48
+ />
49
+ <LkStateLayer />
50
+ </div>
51
+ </template>
@@ -0,0 +1,113 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, useAttrs } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+
5
+ export interface LkNavbarProps {
6
+ material?: LkMaterial
7
+ homeHref?: string
8
+ logoSrc?: string
9
+ logoAlt?: string
10
+ }
11
+
12
+ const props = withDefaults(defineProps<LkNavbarProps>(), {
13
+ material: 'flat',
14
+ homeHref: '/',
15
+ logoSrc: '/logotype.svg',
16
+ logoAlt: '',
17
+ })
18
+
19
+ defineOptions({
20
+ inheritAttrs: false,
21
+ })
22
+
23
+ const attrs = useAttrs()
24
+
25
+ const menuOpen = ref(false)
26
+
27
+ function toggleMenu() {
28
+ menuOpen.value = !menuOpen.value
29
+ }
30
+
31
+ const dataAttrs = computed(() =>
32
+ propsToDataAttrs({ material: props.material }, 'navbar'),
33
+ )
34
+ </script>
35
+
36
+ <template>
37
+ <div
38
+ data-lk-component="navbar"
39
+ v-bind="{ ...dataAttrs, ...attrs }"
40
+ >
41
+ <!-- Desktop Navbar -->
42
+ <div class="navbar-desktop">
43
+ <div data-lk-component="row" data-lk-row-align-items="center" data-lk-row-gap="sm">
44
+ <slot name="brand">
45
+ <a :href="homeHref">
46
+ <img :alt="logoAlt" :src="logoSrc" data-lk-component="image" data-lk-image-height="md" />
47
+ </a>
48
+ </slot>
49
+ </div>
50
+ <div data-lk-component="row">
51
+ <div data-lk-component="row" data-lk-slot="nav-buttons">
52
+ <slot name="navButtons" />
53
+ </div>
54
+ <div data-lk-component="row" data-lk-slot="nav-dropdowns">
55
+ <slot name="navDropdowns" />
56
+ </div>
57
+ </div>
58
+ <div data-lk-component="row" data-lk-navbar-el="nav-menu-end">
59
+ <div data-lk-slot="nav-icon-buttons">
60
+ <slot name="iconButtons" />
61
+ </div>
62
+ <div data-lk-slot="nav-cta-buttons">
63
+ <slot name="ctaButtons" />
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <!-- Mobile Navbar -->
69
+ <div data-lk-navbar-el="nav-menu">
70
+ <div
71
+ data-lk-component="column"
72
+ data-lk-column-align-items="start"
73
+ :class="['navbar-mobile', { active: menuOpen }]"
74
+ >
75
+ <button
76
+ class="navbar-menu-toggle"
77
+ type="button"
78
+ aria-label="Toggle menu"
79
+ :aria-expanded="menuOpen"
80
+ @click="toggleMenu"
81
+ >
82
+ <slot name="menuIcon">
83
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
84
+ </slot>
85
+ </button>
86
+ <slot name="mobileBrand">
87
+ <a :href="homeHref">
88
+ <img :alt="logoAlt" :src="logoSrc" data-lk-component="image" data-lk-image-width="md" data-lk-image-height="md" />
89
+ </a>
90
+ </slot>
91
+ <div v-show="menuOpen" data-lk-component="column">
92
+ <slot name="navButtons" />
93
+ </div>
94
+ <div v-show="menuOpen" data-lk-component="column">
95
+ <slot name="navDropdowns" />
96
+ </div>
97
+ <div v-show="menuOpen">
98
+ <slot name="iconButtons" />
99
+ </div>
100
+ <div v-show="menuOpen" data-lk-component="column" class="flex-h gap-sm">
101
+ <slot name="ctaButtons" />
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Glass material layer -->
107
+ <div
108
+ v-if="material === 'glass'"
109
+ data-lk-component="material-layer"
110
+ data-lk-material-layer-type="glass"
111
+ />
112
+ </div>
113
+ </template>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ inheritAttrs: false,
4
+ })
5
+ </script>
6
+
7
+ <template>
8
+ <div data-lk-component="placeholder-block"></div>
9
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+
5
+ interface LkRowProps {
6
+ alignItems?: 'start' | 'center' | 'end' | 'stretch'
7
+ justifyContent?: 'start' | 'center' | 'end' | 'space-between' | 'space-around'
8
+ gap?: LkSizeUnit
9
+ wrapChildren?: boolean
10
+ defaultChildBehavior?: 'auto-grow' | 'auto-shrink' | 'ignoreFlexRules' | 'ignoreIntrinsicSize'
11
+ }
12
+
13
+ const props = withDefaults(defineProps<LkRowProps>(), {
14
+ alignItems: 'start',
15
+ justifyContent: 'start',
16
+ })
17
+
18
+ defineOptions({
19
+ inheritAttrs: false,
20
+ })
21
+
22
+ const dataAttrs = computed(() =>
23
+ propsToDataAttrs(
24
+ {
25
+ alignItems: props.alignItems,
26
+ justifyContent: props.justifyContent,
27
+ gap: props.gap,
28
+ wrapChildren: props.wrapChildren,
29
+ defaultChildBehavior: props.defaultChildBehavior,
30
+ },
31
+ 'row',
32
+ ),
33
+ )
34
+ </script>
35
+
36
+ <template>
37
+ <div v-bind="{ ...$attrs, ...dataAttrs }" data-lk-component="row">
38
+ <slot />
39
+ </div>
40
+ </template>
@@ -0,0 +1,45 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { propsToDataAttrs } from '../utils/utilities'
4
+
5
+ type SpacingSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'none'
6
+
7
+ interface LkSectionProps {
8
+ padding?: SpacingSize
9
+ px?: SpacingSize
10
+ py?: SpacingSize
11
+ pt?: SpacingSize
12
+ pb?: SpacingSize
13
+ pl?: SpacingSize
14
+ pr?: SpacingSize
15
+ }
16
+
17
+ const props = defineProps<LkSectionProps>()
18
+
19
+ defineOptions({
20
+ inheritAttrs: false,
21
+ })
22
+
23
+ const dataAttrs = computed(() =>
24
+ propsToDataAttrs(
25
+ {
26
+ padding: props.padding,
27
+ px: props.px,
28
+ py: props.py,
29
+ pt: props.pt,
30
+ pb: props.pb,
31
+ pl: props.pl,
32
+ pr: props.pr,
33
+ },
34
+ 'section',
35
+ ),
36
+ )
37
+ </script>
38
+
39
+ <template>
40
+ <section v-bind="{ ...$attrs, ...dataAttrs }">
41
+ <div data-lk-component="section">
42
+ <slot />
43
+ </div>
44
+ </section>
45
+ </template>