@bagelink/vue 1.15.73 → 1.15.75
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/components/AddressSearch.vue.d.ts +3 -0
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/Alert.vue.d.ts +3 -0
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts +17 -2
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts +17 -2
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts +2 -0
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +6 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts +2 -0
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useGradientVariant.d.ts +37 -0
- package/dist/composables/useGradientVariant.d.ts.map +1 -0
- package/dist/index.cjs +47 -47
- package/dist/index.mjs +6217 -6142
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Alert.vue +9 -1
- package/src/components/Badge.vue +37 -5
- package/src/components/Btn.vue +49 -6
- package/src/components/Card.vue +2 -30
- package/src/components/Dropdown.vue +3 -1
- package/src/components/layout/TabsNav.vue +15 -1
- package/src/composables/index.ts +2 -0
- package/src/composables/useGradientVariant.ts +100 -0
- package/src/styles/bagel.css +1 -0
- package/src/styles/base-colors.css +9 -0
- package/src/styles/color-variants.css +149 -0
package/package.json
CHANGED
package/src/components/Alert.vue
CHANGED
|
@@ -10,7 +10,10 @@ type AlertType = 'info' | 'success' | 'warning' | 'error'
|
|
|
10
10
|
interface Props {
|
|
11
11
|
message?: string
|
|
12
12
|
thin?: boolean
|
|
13
|
+
/** Border + transparent background (drops the tinted fill). */
|
|
13
14
|
outline?: boolean
|
|
15
|
+
/** Adds a border while keeping the tinted background. */
|
|
16
|
+
frame?: boolean
|
|
14
17
|
dismissable?: boolean
|
|
15
18
|
type?: AlertType
|
|
16
19
|
/** Boolean shorthands: <Alert error>...</Alert> */
|
|
@@ -42,7 +45,7 @@ const typeIcon: Record<AlertType, IconType> = {
|
|
|
42
45
|
</script>
|
|
43
46
|
|
|
44
47
|
<template>
|
|
45
|
-
<div v-if="!isDismissed" class="alert" :class="[computedType, { thin, outline }]" :dismissable="dismissable">
|
|
48
|
+
<div v-if="!isDismissed" class="alert" :class="[computedType, { thin, outline, frame }]" :dismissable="dismissable">
|
|
46
49
|
<Icon v-if="icon !== 'none'" class="alert_icon" :icon="icon || typeIcon[computedType]" :size="1.7" />
|
|
47
50
|
<slot>
|
|
48
51
|
<p class="m-0">
|
|
@@ -100,6 +103,11 @@ const typeIcon: Record<AlertType, IconType> = {
|
|
|
100
103
|
background: unset;
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
/* frame: border in the alert's accent color, keeps the tinted background. */
|
|
107
|
+
.alert.frame {
|
|
108
|
+
border: 1px solid var(--alert-outline);
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
.alert_icon {
|
|
104
112
|
line-height: 1;
|
|
105
113
|
}
|
package/src/components/Badge.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
defineOptions({ name: 'BglBadge' })
|
|
3
|
-
import type { IconType, ThemeType } from '@bagelink/vue'
|
|
3
|
+
import type { IconType, ThemeType, GradientProp, GradientDirProp } from '@bagelink/vue'
|
|
4
4
|
import type { SetupContext } from 'vue'
|
|
5
|
-
import { Btn, Icon } from '@bagelink/vue'
|
|
5
|
+
import { Btn, Icon, useGradientVariant } from '@bagelink/vue'
|
|
6
6
|
import { computed, useSlots } from 'vue'
|
|
7
7
|
import '../styles/base-colors.css'
|
|
8
8
|
|
|
@@ -17,10 +17,25 @@ const props = defineProps<{
|
|
|
17
17
|
icon?: IconType
|
|
18
18
|
iconEnd?: IconType
|
|
19
19
|
color?: ThemeType
|
|
20
|
-
variant?: 'solid' | 'flat' | 'outline' | 'glass'
|
|
20
|
+
variant?: 'solid' | 'flat' | 'outline' | 'glass' | 'soft' | 'frost'
|
|
21
21
|
/** Boolean variant shorthands: <Badge flat /> — same as variant="flat" */
|
|
22
22
|
flat?: boolean
|
|
23
23
|
outline?: boolean
|
|
24
|
+
/** Soft variant: light tinted bg + full-color text & border, driven by `color`.
|
|
25
|
+
Shorthand for variant="soft". e.g. <Badge color="blue" soft /> */
|
|
26
|
+
soft?: boolean
|
|
27
|
+
/** Frost variant: translucent + blur, tinted by `color`. Reads beautifully
|
|
28
|
+
over photos / gradients / dark heroes. Shorthand for variant="frost". */
|
|
29
|
+
frost?: boolean
|
|
30
|
+
/** Gradient fill (white text). Boolean = auto (a darker shade of `color`);
|
|
31
|
+
or a space-separated tone list — "purple" pairs with `color`
|
|
32
|
+
(blue→purple), "blue purple pink" defines all stops. */
|
|
33
|
+
gradient?: GradientProp
|
|
34
|
+
/** Gradient direction: a named direction ("to-br") or an angle in degrees
|
|
35
|
+
(45 or "45"). Defaults to 135deg. */
|
|
36
|
+
gradientDir?: GradientDirProp
|
|
37
|
+
/** Solid fill + a hairline border (keeps the background, unlike `outline`). */
|
|
38
|
+
frame?: boolean
|
|
24
39
|
/** Translucent frosted badge — readable on photos / gradients / dark heroes.
|
|
25
40
|
Shorthand for variant="glass". */
|
|
26
41
|
glass?: boolean
|
|
@@ -49,9 +64,19 @@ const computedTheme = computed(
|
|
|
49
64
|
},
|
|
50
65
|
)
|
|
51
66
|
|
|
67
|
+
const { isGradient, gradientStyle } = useGradientVariant({
|
|
68
|
+
gradient: () => props.gradient,
|
|
69
|
+
gradientDir: () => props.gradientDir,
|
|
70
|
+
color: () => props.color,
|
|
71
|
+
})
|
|
72
|
+
|
|
52
73
|
const computedPairClass = computed(() => {
|
|
53
74
|
const theme = computedTheme.value
|
|
54
|
-
if (!theme)
|
|
75
|
+
if (!theme) {
|
|
76
|
+
// Frost defaults to a white (light-on-dark) glass when no color is given.
|
|
77
|
+
if (computedVariant.value === 'frost') { return 'pair-white' }
|
|
78
|
+
return 'pair-primary'
|
|
79
|
+
}
|
|
55
80
|
return `pair-${theme}`
|
|
56
81
|
})
|
|
57
82
|
|
|
@@ -64,7 +89,10 @@ const computedSize = computed(() => {
|
|
|
64
89
|
|
|
65
90
|
const computedVariant = computed(() => {
|
|
66
91
|
if (props.variant) { return props.variant }
|
|
92
|
+
if (isGradient.value) { return 'gradient' }
|
|
93
|
+
if (props.frost) { return 'frost' }
|
|
67
94
|
if (props.glass) { return 'glass' }
|
|
95
|
+
if (props.soft) { return 'soft' }
|
|
68
96
|
if (props.flat) { return 'flat' }
|
|
69
97
|
if (props.outline || props.border) { return 'outline' }
|
|
70
98
|
return 'solid'
|
|
@@ -76,7 +104,11 @@ const computedClasses = computed(() => {
|
|
|
76
104
|
'round': props.round,
|
|
77
105
|
'bgl_flatPill': computedVariant.value === 'flat',
|
|
78
106
|
'bgl_pill-border': computedVariant.value === 'outline',
|
|
107
|
+
'bgl_pill-frame': props.frame,
|
|
79
108
|
'bgl_glassPill': computedVariant.value === 'glass',
|
|
109
|
+
'soft': computedVariant.value === 'soft',
|
|
110
|
+
'frost': computedVariant.value === 'frost',
|
|
111
|
+
'gradient': computedVariant.value === 'gradient',
|
|
80
112
|
'pillLarge': computedSize.value === 'lg',
|
|
81
113
|
'pillSmall': computedSize.value === 'sm',
|
|
82
114
|
}
|
|
@@ -91,7 +123,7 @@ const computedClasses = computed(() => {
|
|
|
91
123
|
<template>
|
|
92
124
|
<div
|
|
93
125
|
class="bgl_pill"
|
|
94
|
-
style="height: var(--bgl-pill-height)
|
|
126
|
+
:style="[{ height: 'var(--bgl-pill-height)' }, gradientStyle]"
|
|
95
127
|
:disabled="disabled"
|
|
96
128
|
:class="computedClasses"
|
|
97
129
|
>
|
package/src/components/Btn.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { IconType, ExtendedThemeType, TranslatableString } from '@bagelink/vue'
|
|
2
|
+
import type { IconType, ExtendedThemeType, TranslatableString, GradientProp, GradientDirProp } from '@bagelink/vue'
|
|
3
3
|
import type { SetupContext } from 'vue'
|
|
4
|
-
import { Icon, Loading, useDialog, useI18n, resolveI18n, MOBILE_BREAKPOINT } from '@bagelink/vue'
|
|
4
|
+
import { Icon, Loading, useDialog, useI18n, resolveI18n, useGradientVariant, MOBILE_BREAKPOINT } from '@bagelink/vue'
|
|
5
5
|
import { useSlots, ref, onMounted, onUnmounted, computed } from 'vue'
|
|
6
6
|
import { RouterLink } from 'vue-router'
|
|
7
7
|
defineOptions({ name: 'BglBtn' })
|
|
@@ -13,10 +13,25 @@ const props = withDefaults(
|
|
|
13
13
|
iconSize?: number | string
|
|
14
14
|
iconMobileSize?: number | string
|
|
15
15
|
color?: ExtendedThemeType
|
|
16
|
-
variant?: 'solid' | 'flat' | 'outline'
|
|
16
|
+
variant?: 'solid' | 'flat' | 'outline' | 'soft' | 'frost'
|
|
17
17
|
/** Boolean variant shorthands: <Btn flat /> — same as variant="flat" */
|
|
18
18
|
flat?: boolean
|
|
19
19
|
outline?: boolean
|
|
20
|
+
/** Soft variant: light tinted bg + full-color text & border, driven by `color`.
|
|
21
|
+
Shorthand for variant="soft". e.g. <Btn color="blue" soft /> */
|
|
22
|
+
soft?: boolean
|
|
23
|
+
/** Frost variant: translucent + blur, tinted by `color`. Reads beautifully
|
|
24
|
+
over photos / gradients / dark heroes. Shorthand for variant="frost". */
|
|
25
|
+
frost?: boolean
|
|
26
|
+
/** Gradient fill (white text). Boolean = auto (a darker shade of `color`);
|
|
27
|
+
or a space-separated tone list — "purple" pairs with `color`
|
|
28
|
+
(blue→purple), "blue purple pink" defines all stops. */
|
|
29
|
+
gradient?: GradientProp
|
|
30
|
+
/** Gradient direction: a named direction ("to-br") or an angle in degrees
|
|
31
|
+
(45 or "45"). Defaults to 135deg. */
|
|
32
|
+
gradientDir?: GradientDirProp
|
|
33
|
+
/** Solid fill + a hairline border (keeps the background, unlike `outline`). */
|
|
34
|
+
frame?: boolean
|
|
20
35
|
/** @deprecated Use `outline` */
|
|
21
36
|
border?: boolean
|
|
22
37
|
thin?: boolean
|
|
@@ -67,17 +82,41 @@ const emit = defineEmits<{
|
|
|
67
82
|
|
|
68
83
|
const { $t } = useI18n()
|
|
69
84
|
|
|
85
|
+
const { isGradient, gradientStyle } = useGradientVariant({
|
|
86
|
+
gradient: () => props.gradient,
|
|
87
|
+
gradientDir: () => props.gradientDir,
|
|
88
|
+
color: () => props.color,
|
|
89
|
+
})
|
|
90
|
+
|
|
70
91
|
const computedVariant = computed(() => {
|
|
71
92
|
if (props.variant) { return props.variant }
|
|
72
|
-
if (
|
|
93
|
+
if (isGradient.value) { return 'gradient' }
|
|
94
|
+
if (props.frost) { return 'frost' }
|
|
95
|
+
if (props.soft) { return 'soft' }
|
|
96
|
+
// An explicit `outline`/`border` wins over `flat` — components like Dropdown
|
|
97
|
+
// default the trigger to `flat`, so `<Dropdown outline>` must still resolve to
|
|
98
|
+
// the outline variant rather than being swallowed by the inherited flat.
|
|
73
99
|
if (props.outline || props.border) { return 'outline' }
|
|
100
|
+
// `frame` is "solid fill + a border", so it also overrides an inherited
|
|
101
|
+
// `flat` (e.g. Dropdown's flat trigger) to restore the solid background.
|
|
102
|
+
if (props.frame) { return 'solid' }
|
|
103
|
+
if (props.flat) { return 'flat' }
|
|
74
104
|
return 'solid'
|
|
75
105
|
})
|
|
76
106
|
|
|
77
107
|
const computedPairClass = computed(() => {
|
|
78
108
|
const theme = props.color
|
|
79
109
|
if (!theme) {
|
|
80
|
-
//
|
|
110
|
+
// Frost defaults to a white (light-on-dark) glass when no color is given.
|
|
111
|
+
if (computedVariant.value === 'frost') {
|
|
112
|
+
return 'pair-white'
|
|
113
|
+
}
|
|
114
|
+
// Gradient with explicit stops (e.g. gradient="blue purple") needs a pair
|
|
115
|
+
// class only to activate `.gradient`; default to primary for the auto case.
|
|
116
|
+
if (computedVariant.value === 'gradient') {
|
|
117
|
+
return 'pair-primary'
|
|
118
|
+
}
|
|
119
|
+
// Only flat/outline/soft buttons get a default for visibility
|
|
81
120
|
if (computedVariant.value !== 'solid') {
|
|
82
121
|
return 'pair-black'
|
|
83
122
|
}
|
|
@@ -181,7 +220,11 @@ const slots: SetupContext['slots'] = useSlots()
|
|
|
181
220
|
round,
|
|
182
221
|
'bgl_flatPill': computedVariant === 'flat',
|
|
183
222
|
'bgl_pill-border': computedVariant === 'outline',
|
|
184
|
-
|
|
223
|
+
'soft': computedVariant === 'soft',
|
|
224
|
+
'frost': computedVariant === 'frost',
|
|
225
|
+
'gradient': computedVariant === 'gradient',
|
|
226
|
+
'bgl_pill-frame': frame,
|
|
227
|
+
}, computedPairClass]" :style="gradientStyle" :tabindex="disabled ? -1 : 0" @click.stop="handleClick" @keydown.enter="handleClick" @keydown.space="handleClick"
|
|
185
228
|
>
|
|
186
229
|
<Loading v-if="loading" class="h-100p" size="15" color="currentColor" />
|
|
187
230
|
<div v-else class="bgl_btn-flex">
|
package/src/components/Card.vue
CHANGED
|
@@ -49,7 +49,7 @@ const is = computed(() => {
|
|
|
49
49
|
<component
|
|
50
50
|
:is="is" v-ripple="!!to" v-bind="bind" class="bgl_card" :class="{
|
|
51
51
|
thin,
|
|
52
|
-
'border': outline,
|
|
52
|
+
'border bg-transparent': outline,
|
|
53
53
|
'h-100': h100,
|
|
54
54
|
[bg || '']: bg,
|
|
55
55
|
'overflow-x': overflowX,
|
|
@@ -57,7 +57,7 @@ const is = computed(() => {
|
|
|
57
57
|
'card_frame': frame,
|
|
58
58
|
}"
|
|
59
59
|
>
|
|
60
|
-
<span v-if="label" class="card_label">
|
|
60
|
+
<span v-if="label" class="card_label block label">
|
|
61
61
|
{{ label }}
|
|
62
62
|
</span>
|
|
63
63
|
<!-- Header row: title + optional trailing action. Full override via #header. -->
|
|
@@ -76,15 +76,6 @@ const is = computed(() => {
|
|
|
76
76
|
.card_frame {
|
|
77
77
|
border: 1px solid var(--bgl-border-color);
|
|
78
78
|
}
|
|
79
|
-
.card_label {
|
|
80
|
-
font-size: 1rem;
|
|
81
|
-
position: relative;
|
|
82
|
-
top: -0.5rem;
|
|
83
|
-
padding: 0.75rem 0;
|
|
84
|
-
display: block;
|
|
85
|
-
border-bottom: 1px solid var(--bgl-border-color);
|
|
86
|
-
margin-bottom: 1rem;
|
|
87
|
-
}
|
|
88
79
|
|
|
89
80
|
/* Header row (title + action). Pulls out to the card edges via negative margins
|
|
90
81
|
so its divider spans full-width regardless of the card's own padding, then
|
|
@@ -99,20 +90,6 @@ min-height: 0;
|
|
|
99
90
|
font-weight: 600;
|
|
100
91
|
}
|
|
101
92
|
|
|
102
|
-
.border .card_label {
|
|
103
|
-
font-size: 0.7rem;
|
|
104
|
-
font-weight: 300;
|
|
105
|
-
background: var(--bgl-box-bg);
|
|
106
|
-
padding: 0 0.75rem;
|
|
107
|
-
position: absolute;
|
|
108
|
-
top: -0.5rem;
|
|
109
|
-
inset-inline-start: 1rem;
|
|
110
|
-
border-left: 1px solid var(--bgl-border-color);
|
|
111
|
-
border-right: 1px solid var(--bgl-border-color);
|
|
112
|
-
border-bottom: unset;
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
93
|
.bgl_card {
|
|
117
94
|
--bgl-card-pad: 2rem;
|
|
118
95
|
border-radius: var(--bgl-card-border-radius);
|
|
@@ -125,11 +102,6 @@ position: relative;
|
|
|
125
102
|
background: var(--bgl-gray-tint);
|
|
126
103
|
}
|
|
127
104
|
|
|
128
|
-
.bgl_card.border {
|
|
129
|
-
border: 1px solid var(--bgl-border-color);
|
|
130
|
-
background-color: transparent;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
105
|
.bgl_card.thin {
|
|
134
106
|
--bgl-card-pad: 1rem;
|
|
135
107
|
padding: var(--bgl-card-pad);
|
|
@@ -27,6 +27,8 @@ const props = withDefaults(defineProps<{
|
|
|
27
27
|
iconEnd?: IconType
|
|
28
28
|
border?: boolean
|
|
29
29
|
outline?: boolean
|
|
30
|
+
/** Solid fill + a hairline border on the trigger (keeps the background, unlike `outline`). */
|
|
31
|
+
frame?: boolean
|
|
30
32
|
round?: boolean
|
|
31
33
|
placement?: AlignedPlacement
|
|
32
34
|
disablePlacement?: boolean
|
|
@@ -386,7 +388,7 @@ defineExpose({ show, hide, shown })
|
|
|
386
388
|
>
|
|
387
389
|
<slot name="trigger" :show :hide :shown>
|
|
388
390
|
<Btn
|
|
389
|
-
:class="triggerClass" :iconEnd :icon="iconSet" :value :thin :flat :outline :round :color
|
|
391
|
+
:class="triggerClass" :iconEnd :icon="iconSet" :value :thin :flat :outline :frame :round :color
|
|
390
392
|
:disabled @click="onTriggerClick"
|
|
391
393
|
/>
|
|
392
394
|
</slot>
|
|
@@ -23,6 +23,8 @@ const props = defineProps<{
|
|
|
23
23
|
alignTxt?: 'center' | 'start' | 'end'
|
|
24
24
|
alignTxtMobile?: 'center' | 'start' | 'end'
|
|
25
25
|
outline?: boolean
|
|
26
|
+
/** Adds a border around the tabs wrapper (keeps the background). */
|
|
27
|
+
frame?: boolean
|
|
26
28
|
}>()
|
|
27
29
|
|
|
28
30
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -140,7 +142,7 @@ onBeforeUnmount(() => {
|
|
|
140
142
|
</script>
|
|
141
143
|
|
|
142
144
|
<template>
|
|
143
|
-
<div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical, 'outline': outline }">
|
|
145
|
+
<div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical, 'bgl_tabs-outline': outline, 'bgl_tabs-frame': frame }">
|
|
144
146
|
<slot name="tabs" v-bind="{ selectTab, isActive, tabLabel, tabs: tabEls }">
|
|
145
147
|
<button v-for="(tab, i) in props.tabs" :key="i" type="button" :class="[
|
|
146
148
|
{ active: isActive(tab) },
|
|
@@ -200,6 +202,18 @@ border-radius: calc(var(--bgl_tabs-border-radius) * 1.4);
|
|
|
200
202
|
gap: var(--bgl_tabs-gap);
|
|
201
203
|
}
|
|
202
204
|
|
|
205
|
+
/* outline: border + drop the filled background & shadow (transparent shell). */
|
|
206
|
+
.bgl_tabs-outline.bgl_tabs_wrap {
|
|
207
|
+
border: 1px solid var(--bgl-border-color);
|
|
208
|
+
background: transparent;
|
|
209
|
+
box-shadow: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* frame: border on top of the normal filled background (keeps bg & shadow). */
|
|
213
|
+
.bgl_tabs-frame.bgl_tabs_wrap {
|
|
214
|
+
border: 1px solid var(--bgl-border-color);
|
|
215
|
+
}
|
|
216
|
+
|
|
203
217
|
.bgl_tab {
|
|
204
218
|
border: none;
|
|
205
219
|
background: transparent;
|
package/src/composables/index.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { useAddToCalendar } from './useAddToCalendar'
|
|
|
8
8
|
export { useDevice } from './useDevice'
|
|
9
9
|
export { useEscapeKey } from './useEscapeKey'
|
|
10
10
|
export { useExcel } from './useExcel'
|
|
11
|
+
export type { GradientDir, GradientDirProp, GradientProp } from './useGradientVariant'
|
|
12
|
+
export { useGradientVariant } from './useGradientVariant'
|
|
11
13
|
export { useLocalStore } from './useLocalStore'
|
|
12
14
|
export { usePolling } from './usePolling'
|
|
13
15
|
export { useQuery } from './useQuery'
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import { computed, toValue } from 'vue'
|
|
3
|
+
|
|
4
|
+
/** The 8 named directions, mirroring the `.to-*` utilities in gradients.css. */
|
|
5
|
+
export type GradientDir
|
|
6
|
+
= 'to-t' | 'to-b' | 'to-l' | 'to-r' | 'to-tl' | 'to-tr' | 'to-bl' | 'to-br'
|
|
7
|
+
|
|
8
|
+
/** `gradient` prop: boolean (auto from `color`) or a space-separated tone list,
|
|
9
|
+
* e.g. "purple" (2-stop with `color`) or "blue purple pink" (full control). */
|
|
10
|
+
export type GradientProp = boolean | string
|
|
11
|
+
|
|
12
|
+
/** `gradient-dir` prop: a named direction, or an angle in degrees (number or
|
|
13
|
+
* numeric string). Undefined → CSS default (135deg). */
|
|
14
|
+
export type GradientDirProp = GradientDir | number | string
|
|
15
|
+
|
|
16
|
+
const DIR_MAP: Record<GradientDir, string> = {
|
|
17
|
+
'to-t': 'to top',
|
|
18
|
+
'to-b': 'to bottom',
|
|
19
|
+
'to-l': 'to left',
|
|
20
|
+
'to-r': 'to right',
|
|
21
|
+
'to-tl': 'to top left',
|
|
22
|
+
'to-tr': 'to top right',
|
|
23
|
+
'to-bl': 'to bottom left',
|
|
24
|
+
'to-br': 'to bottom right',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Resolve a gradient-dir value to a CSS angle/keyword, or undefined for default. */
|
|
28
|
+
function resolveAngle(dir: GradientDirProp | undefined): string | undefined {
|
|
29
|
+
if (dir == null || dir === '') { return undefined }
|
|
30
|
+
if (typeof dir === 'string' && dir in DIR_MAP) {
|
|
31
|
+
return DIR_MAP[dir as GradientDir]
|
|
32
|
+
}
|
|
33
|
+
const n = Number(dir)
|
|
34
|
+
return Number.isFinite(n) ? `${n}deg` : undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Split a tone string ("blue purple pink") into individual tone tokens. */
|
|
38
|
+
function parseTones(value: GradientProp | undefined): string[] {
|
|
39
|
+
if (typeof value !== 'string') { return [] }
|
|
40
|
+
return value.trim().split(/\s+/).filter(Boolean)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface UseGradientVariantArgs {
|
|
44
|
+
/** The `gradient` prop value. */
|
|
45
|
+
gradient: MaybeRefOrGetter<GradientProp | undefined>
|
|
46
|
+
/** The `gradient-dir` prop value. */
|
|
47
|
+
gradientDir?: MaybeRefOrGetter<GradientDirProp | undefined>
|
|
48
|
+
/** The component's `color` prop — used as the first stop when present. */
|
|
49
|
+
color?: MaybeRefOrGetter<string | undefined>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Shared logic for the `gradient` variant across Btn / Badge (and anything else).
|
|
54
|
+
*
|
|
55
|
+
* Stop resolution:
|
|
56
|
+
* - `color` (if set) is the first stop, followed by tones from `gradient`.
|
|
57
|
+
* - With no `color`, all stops come from `gradient`.
|
|
58
|
+
* - A single resolved stop (boolean `gradient`, or one tone) lets the CSS
|
|
59
|
+
* `.gradient` modifier auto-derive a darker second stop — so we inject
|
|
60
|
+
* nothing and rely on its `--bgl-grad-default-*` fallback.
|
|
61
|
+
*
|
|
62
|
+
* Returns the inline CSS custom properties to bind via `:style`. Named direction
|
|
63
|
+
* / via / extra stops authored as gradients.css classes still compose on top.
|
|
64
|
+
*/
|
|
65
|
+
export function useGradientVariant({ gradient, gradientDir, color }: UseGradientVariantArgs) {
|
|
66
|
+
/** Whether the gradient variant is active at all. */
|
|
67
|
+
const isGradient = computed(() => {
|
|
68
|
+
const g = toValue(gradient)
|
|
69
|
+
return g === true || (typeof g === 'string' && g.trim() !== '')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const stops = computed(() => {
|
|
73
|
+
const c = toValue(color)
|
|
74
|
+
const tones = parseTones(toValue(gradient))
|
|
75
|
+
return [c, ...tones].filter(Boolean) as string[]
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const gradientStyle = computed<Record<string, string>>(() => {
|
|
79
|
+
if (!isGradient.value) { return {} }
|
|
80
|
+
|
|
81
|
+
const style: Record<string, string> = {}
|
|
82
|
+
const angle = resolveAngle(toValue(gradientDir))
|
|
83
|
+
if (angle) { style['--bgl-grad-angle'] = angle }
|
|
84
|
+
|
|
85
|
+
const s = stops.value
|
|
86
|
+
// 0–1 stops → let the CSS auto-derive a darker second stop. Inject nothing.
|
|
87
|
+
if (s.length < 2) { return style }
|
|
88
|
+
|
|
89
|
+
style['--bgl-grad-from'] = `var(--bgl-${s[0]})`
|
|
90
|
+
style['--bgl-grad-to'] = `var(--bgl-${s[s.length - 1]})`
|
|
91
|
+
// Middle stops become the `via` slot (comma-terminated, like gradients.css).
|
|
92
|
+
const middle = s.slice(1, -1)
|
|
93
|
+
if (middle.length) {
|
|
94
|
+
style['--bgl-grad-via'] = `${middle.map(t => `var(--bgl-${t})`).join(', ')}, `
|
|
95
|
+
}
|
|
96
|
+
return style
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return { isGradient, gradientStyle, stops }
|
|
100
|
+
}
|
package/src/styles/bagel.css
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
@import "mobileColors.css";
|
|
35
35
|
@import "appearance.css";
|
|
36
36
|
@import "gradients.css";
|
|
37
|
+
@import "color-variants.css";
|
|
37
38
|
|
|
38
39
|
/* Icon font-family bindings — mirrored from Icon.vue so icons work even when
|
|
39
40
|
that component's injected <style> is not yet present (e.g. Vite dev mode). */
|
|
@@ -1514,6 +1514,15 @@
|
|
|
1514
1514
|
outline: 1px solid;
|
|
1515
1515
|
}
|
|
1516
1516
|
|
|
1517
|
+
/* frame: keeps the solid fill but adds a hairline border on top.
|
|
1518
|
+
Unlike .bgl_pill-border (outline variant) it does NOT clear the background.
|
|
1519
|
+
The border tracks the pill's own tone (falling back to the neutral border
|
|
1520
|
+
color) so a filled primary button gets a primary-toned edge, not a stray
|
|
1521
|
+
grey ring that reads like an outline. */
|
|
1522
|
+
.bgl_pill-frame {
|
|
1523
|
+
border: 1px solid color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-border-color)) 55%, transparent);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1517
1526
|
/* Base colors flat/border */
|
|
1518
1527
|
.pair-blue.bgl_flatPill,
|
|
1519
1528
|
.pair-blue.bgl_pill-border {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
* COLOR VARIANTS — soft (+ future: frost / gradient)
|
|
3
|
+
*
|
|
4
|
+
* These are *modifiers* that compose on top of any `.pair-{color}` class, the
|
|
5
|
+
* same way `.bgl_flatPill` / `.bgl_pill-border` already do. Components keep
|
|
6
|
+
* computing `pair-${color}` and simply add `.soft` — nothing about the existing
|
|
7
|
+
* color system changes.
|
|
8
|
+
*
|
|
9
|
+
* SOFT = light tinted background + full-color text + full-color border.
|
|
10
|
+
* Equivalent to: bg-{color}-30 color-{color} border border-{color}
|
|
11
|
+
*
|
|
12
|
+
* To stay DRY we expose one variable per tone — `--bgl-pair-tone` — set once for
|
|
13
|
+
* every shade of a color, then `.soft` derives bg / text / border from it via
|
|
14
|
+
* color-mix(). One rule covers all 13 tones × all 13 shades.
|
|
15
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
16
|
+
|
|
17
|
+
/* 1) Map every pair tone (base + 10..130 + light/tint/dark + semantic) to a
|
|
18
|
+
* single --bgl-pair-tone custom property. */
|
|
19
|
+
.pair-primary, .pair-default,
|
|
20
|
+
.pair-primary-light, .pair-primary-tint,
|
|
21
|
+
.pair-primary-10, .pair-primary-20, .pair-primary-30, .pair-primary-40, .pair-primary-50,
|
|
22
|
+
.pair-primary-60, .pair-primary-70, .pair-primary-80, .pair-primary-90, .pair-primary-100,
|
|
23
|
+
.pair-primary-110, .pair-primary-120, .pair-primary-130 { --bgl-pair-tone: var(--bgl-primary); }
|
|
24
|
+
|
|
25
|
+
.pair-blue, .pair-info,
|
|
26
|
+
.pair-blue-light, .pair-blue-tint, .pair-blue-dark,
|
|
27
|
+
.pair-blue-10, .pair-blue-20, .pair-blue-30, .pair-blue-40, .pair-blue-50,
|
|
28
|
+
.pair-blue-60, .pair-blue-70, .pair-blue-80, .pair-blue-90, .pair-blue-100,
|
|
29
|
+
.pair-blue-110, .pair-blue-120, .pair-blue-130 { --bgl-pair-tone: var(--bgl-blue); }
|
|
30
|
+
|
|
31
|
+
.pair-green, .pair-success,
|
|
32
|
+
.pair-green-light,
|
|
33
|
+
.pair-green-10, .pair-green-20, .pair-green-30, .pair-green-40, .pair-green-50,
|
|
34
|
+
.pair-green-60, .pair-green-70, .pair-green-80, .pair-green-90, .pair-green-100,
|
|
35
|
+
.pair-green-110, .pair-green-120, .pair-green-130 { --bgl-pair-tone: var(--bgl-green); }
|
|
36
|
+
|
|
37
|
+
.pair-red, .pair-danger,
|
|
38
|
+
.pair-red-light, .pair-red-tint,
|
|
39
|
+
.pair-red-10, .pair-red-20, .pair-red-30, .pair-red-40, .pair-red-50,
|
|
40
|
+
.pair-red-60, .pair-red-70, .pair-red-80, .pair-red-90, .pair-red-100,
|
|
41
|
+
.pair-red-110, .pair-red-120, .pair-red-130 { --bgl-pair-tone: var(--bgl-red); }
|
|
42
|
+
|
|
43
|
+
.pair-yellow, .pair-warning,
|
|
44
|
+
.pair-yellow-light,
|
|
45
|
+
.pair-yellow-10, .pair-yellow-20, .pair-yellow-30, .pair-yellow-40, .pair-yellow-50,
|
|
46
|
+
.pair-yellow-60, .pair-yellow-70, .pair-yellow-80, .pair-yellow-90, .pair-yellow-100,
|
|
47
|
+
.pair-yellow-110, .pair-yellow-120, .pair-yellow-130 { --bgl-pair-tone: var(--bgl-yellow); }
|
|
48
|
+
|
|
49
|
+
.pair-purple,
|
|
50
|
+
.pair-purple-light,
|
|
51
|
+
.pair-purple-10, .pair-purple-20, .pair-purple-30, .pair-purple-40, .pair-purple-50,
|
|
52
|
+
.pair-purple-60, .pair-purple-70, .pair-purple-80, .pair-purple-90, .pair-purple-100,
|
|
53
|
+
.pair-purple-110, .pair-purple-120, .pair-purple-130 { --bgl-pair-tone: var(--bgl-purple); }
|
|
54
|
+
|
|
55
|
+
.pair-brown,
|
|
56
|
+
.pair-brown-light,
|
|
57
|
+
.pair-brown-10, .pair-brown-20, .pair-brown-30, .pair-brown-40, .pair-brown-50,
|
|
58
|
+
.pair-brown-60, .pair-brown-70, .pair-brown-80, .pair-brown-90, .pair-brown-100,
|
|
59
|
+
.pair-brown-110, .pair-brown-120, .pair-brown-130 { --bgl-pair-tone: var(--bgl-brown); }
|
|
60
|
+
|
|
61
|
+
.pair-orange,
|
|
62
|
+
.pair-orange-light,
|
|
63
|
+
.pair-orange-10, .pair-orange-20, .pair-orange-30, .pair-orange-40, .pair-orange-50,
|
|
64
|
+
.pair-orange-60, .pair-orange-70, .pair-orange-80, .pair-orange-90, .pair-orange-100,
|
|
65
|
+
.pair-orange-110, .pair-orange-120, .pair-orange-130 { --bgl-pair-tone: var(--bgl-orange); }
|
|
66
|
+
|
|
67
|
+
.pair-turquoise,
|
|
68
|
+
.pair-turquoise-light,
|
|
69
|
+
.pair-turquoise-10, .pair-turquoise-20, .pair-turquoise-30, .pair-turquoise-40, .pair-turquoise-50,
|
|
70
|
+
.pair-turquoise-60, .pair-turquoise-70, .pair-turquoise-80, .pair-turquoise-90, .pair-turquoise-100,
|
|
71
|
+
.pair-turquoise-110, .pair-turquoise-120, .pair-turquoise-130 { --bgl-pair-tone: var(--bgl-turquoise); }
|
|
72
|
+
|
|
73
|
+
.pair-pink,
|
|
74
|
+
.pair-pink-light,
|
|
75
|
+
.pair-pink-10, .pair-pink-20, .pair-pink-30, .pair-pink-40, .pair-pink-50,
|
|
76
|
+
.pair-pink-60, .pair-pink-70, .pair-pink-80, .pair-pink-90, .pair-pink-100,
|
|
77
|
+
.pair-pink-110, .pair-pink-120, .pair-pink-130 { --bgl-pair-tone: var(--bgl-pink); }
|
|
78
|
+
|
|
79
|
+
.pair-gray,
|
|
80
|
+
.pair-gray-light, .pair-gray-tint,
|
|
81
|
+
.pair-gray-10, .pair-gray-20, .pair-gray-30, .pair-gray-40, .pair-gray-50,
|
|
82
|
+
.pair-gray-60, .pair-gray-70, .pair-gray-80, .pair-gray-90, .pair-gray-100,
|
|
83
|
+
.pair-gray-110, .pair-gray-120, .pair-gray-130 { --bgl-pair-tone: var(--bgl-gray); }
|
|
84
|
+
|
|
85
|
+
.pair-black,
|
|
86
|
+
.pair-black-tint,
|
|
87
|
+
.pair-black-10, .pair-black-20, .pair-black-30, .pair-black-40, .pair-black-50,
|
|
88
|
+
.pair-black-60, .pair-black-70, .pair-black-80, .pair-black-90, .pair-black-100,
|
|
89
|
+
.pair-black-110, .pair-black-120, .pair-black-130 { --bgl-pair-tone: var(--bgl-black); }
|
|
90
|
+
|
|
91
|
+
.pair-white { --bgl-pair-tone: var(--bgl-white); }
|
|
92
|
+
|
|
93
|
+
/* 2) SOFT modifier — applies on any element carrying a pair-* class.
|
|
94
|
+
* bg ≈ 18% tint, border ≈ full tone, text = full tone. */
|
|
95
|
+
[class*="pair-"].soft {
|
|
96
|
+
background-color: color-mix(in srgb, var(--bgl-pair-tone) 18%, transparent) !important;
|
|
97
|
+
color: var(--bgl-pair-tone) !important;
|
|
98
|
+
border: 1px solid var(--bgl-pair-tone) !important;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* White is special: a white tone on a light surface needs dark text/border. */
|
|
102
|
+
.pair-white.soft {
|
|
103
|
+
color: var(--bgl-black) !important;
|
|
104
|
+
border-color: var(--bgl-black) !important;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* 3) FROST modifier — translucent + blur, tinted by the pair color.
|
|
108
|
+
* Like `soft` but glassier: lower-opacity fill, a blurred backdrop, and a
|
|
109
|
+
* faint same-tone border. Shines over photos / gradients / dark heroes.
|
|
110
|
+
* Text uses the full tone so it stays legible on the light translucent fill. */
|
|
111
|
+
[class*="pair-"].frost {
|
|
112
|
+
/* `--bgl-pair-tone` falls back to white so a frost with no color (or before a
|
|
113
|
+
tone is mapped) renders as the classic light-on-dark glass. */
|
|
114
|
+
background-color: color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-white)) 14%, transparent) !important;
|
|
115
|
+
color: var(--bgl-pair-tone, var(--bgl-white)) !important;
|
|
116
|
+
border: 1px solid color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-white)) 35%, transparent) !important;
|
|
117
|
+
backdrop-filter: blur(8px);
|
|
118
|
+
-webkit-backdrop-filter: blur(8px);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.pair-white.frost {
|
|
122
|
+
color: var(--bgl-white) !important;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* 4) GRADIENT modifier — a colored linear gradient + white text.
|
|
126
|
+
*
|
|
127
|
+
* Works two ways, both pure CSS so it applies to <div> as well as components:
|
|
128
|
+
*
|
|
129
|
+
* a) Automatic (just a pair class): sweeps from the tone to a darker shade of
|
|
130
|
+
* itself. <div class="pair-blue gradient">
|
|
131
|
+
*
|
|
132
|
+
* b) Explicit stops: provide --bgl-grad-from / --bgl-grad-via / --bgl-grad-to
|
|
133
|
+
* (and optionally --bgl-grad-angle). The Btn/Badge components inject these
|
|
134
|
+
* from their `gradient` / `gradient-dir` props, and the existing
|
|
135
|
+
* gradients.css utilities (from-*, via-*, to-*, to-tr, …) compose too.
|
|
136
|
+
*
|
|
137
|
+
* The `via` slot collapses to nothing when unset (2-stop), expands when set
|
|
138
|
+
* (3-stop) — exactly like gradients.css. */
|
|
139
|
+
[class*="pair-"].gradient {
|
|
140
|
+
--bgl-grad-default-from: var(--bgl-pair-tone, var(--bgl-primary));
|
|
141
|
+
--bgl-grad-default-to: color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-primary)) 55%, #000);
|
|
142
|
+
background-image: linear-gradient(
|
|
143
|
+
var(--bgl-grad-angle, 135deg),
|
|
144
|
+
var(--bgl-grad-from, var(--bgl-grad-default-from)),
|
|
145
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to, var(--bgl-grad-default-to))
|
|
146
|
+
) !important;
|
|
147
|
+
color: var(--bgl-white) !important;
|
|
148
|
+
border: none !important;
|
|
149
|
+
}
|