@citizenplane/pimp 18.0.6 → 18.1.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/dist/components/CpButtonToggle.vue.d.ts +30 -0
- package/dist/components/CpButtonToggle.vue.d.ts.map +1 -0
- package/dist/components/CpSwitch.vue.d.ts +2 -0
- package/dist/components/CpSwitch.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/pimp.es.js +1508 -1454
- package/dist/pimp.umd.js +51 -51
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/CpButtonToggle.vue +137 -0
- package/src/components/CpSwitch.vue +13 -1
- package/src/components/index.ts +3 -0
- package/src/stories/CpButtonToggle.stories.ts +137 -0
package/package.json
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
:aria-disabled="disabled"
|
|
4
|
+
:aria-pressed="isSelected"
|
|
5
|
+
class="cpToggleButton"
|
|
6
|
+
:class="dynamicClasses"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
type="button"
|
|
9
|
+
@click="handleClick"
|
|
10
|
+
>
|
|
11
|
+
<span class="cpToggleButton__leading">
|
|
12
|
+
<slot name="leading">
|
|
13
|
+
<cp-icon size="16" :type="leadingIcon" />
|
|
14
|
+
</slot>
|
|
15
|
+
</span>
|
|
16
|
+
<span v-if="showLabel" class="cpToggleButton__label">
|
|
17
|
+
<slot>
|
|
18
|
+
{{ label }}
|
|
19
|
+
</slot>
|
|
20
|
+
</span>
|
|
21
|
+
</button>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { computed, useSlots } from 'vue'
|
|
26
|
+
|
|
27
|
+
interface Props {
|
|
28
|
+
disabled?: boolean
|
|
29
|
+
isSelected?: boolean
|
|
30
|
+
label?: string
|
|
31
|
+
leadingIcon?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
35
|
+
isSelected: false,
|
|
36
|
+
disabled: false,
|
|
37
|
+
label: '',
|
|
38
|
+
leadingIcon: 'dashed-circle',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const emit = defineEmits<{
|
|
42
|
+
click: [event: MouseEvent]
|
|
43
|
+
}>()
|
|
44
|
+
|
|
45
|
+
const slots = useSlots()
|
|
46
|
+
|
|
47
|
+
const showLabel = computed(() => !!props.label || !!slots.default)
|
|
48
|
+
|
|
49
|
+
const dynamicClasses = computed(() => ({
|
|
50
|
+
'cpToggleButton--isSelected': props.isSelected,
|
|
51
|
+
}))
|
|
52
|
+
|
|
53
|
+
const handleClick = (event: MouseEvent) => {
|
|
54
|
+
emit('click', event)
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<style lang="scss">
|
|
59
|
+
.cpToggleButton {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
gap: var(--cp-spacing-md);
|
|
64
|
+
padding: var(--cp-spacing-lg);
|
|
65
|
+
border-radius: var(--cp-radius-md);
|
|
66
|
+
box-shadow:
|
|
67
|
+
var(--cp-shadows-3xs-inset),
|
|
68
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);
|
|
69
|
+
background-color: var(--cp-background-primary);
|
|
70
|
+
color: var(--cp-text-secondary);
|
|
71
|
+
transition:
|
|
72
|
+
background-color 100ms ease-out,
|
|
73
|
+
box-shadow 100ms ease-out,
|
|
74
|
+
color 100ms ease-out;
|
|
75
|
+
|
|
76
|
+
&:hover:not(:disabled) {
|
|
77
|
+
background-color: var(--cp-background-primary-hover);
|
|
78
|
+
color: var(--cp-text-secondary-hover);
|
|
79
|
+
box-shadow:
|
|
80
|
+
var(--cp-shadows-3xs-inset),
|
|
81
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&:focus-visible {
|
|
85
|
+
box-shadow:
|
|
86
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover),
|
|
87
|
+
var(--cp-shadow-focus-ring-accent);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&__leading {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&__label {
|
|
96
|
+
font-size: var(--cp-text-size-sm);
|
|
97
|
+
line-height: var(--cp-line-height-sm);
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&__defaultLeading,
|
|
102
|
+
&__defaultLeading * {
|
|
103
|
+
pointer-events: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&:disabled {
|
|
107
|
+
cursor: not-allowed;
|
|
108
|
+
background-color: var(--cp-background-disabled);
|
|
109
|
+
color: var(--cp-text-disabled);
|
|
110
|
+
box-shadow:
|
|
111
|
+
var(--cp-shadows-3xs-inset),
|
|
112
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-disabled);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&--isSelected:not(:disabled) {
|
|
116
|
+
background-color: var(--cp-background-accent-secondary);
|
|
117
|
+
color: var(--cp-text-accent-primary);
|
|
118
|
+
box-shadow:
|
|
119
|
+
var(--cp-shadows-3xs-inset),
|
|
120
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-accent-solid);
|
|
121
|
+
|
|
122
|
+
&:hover {
|
|
123
|
+
background-color: var(--cp-background-accent-secondary-hover);
|
|
124
|
+
color: var(--cp-text-accent-primary-hover);
|
|
125
|
+
box-shadow:
|
|
126
|
+
var(--cp-shadows-3xs-inset),
|
|
127
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-accent-solid);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&:focus-visible {
|
|
131
|
+
box-shadow:
|
|
132
|
+
0 0 0 var(--cp-dimensions-0_25) var(--cp-border-accent-solid),
|
|
133
|
+
var(--cp-shadow-focus-ring-accent);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<label class="cpSwitch" :class="computedClasses" :for="switchUniqueId">
|
|
2
|
+
<label class="cpSwitch" :class="computedClasses" :for="switchUniqueId" v-bind="inertProps">
|
|
3
3
|
<span class="cpSwitch__switch">
|
|
4
4
|
<input
|
|
5
5
|
:id="switchUniqueId"
|
|
@@ -46,6 +46,7 @@ interface Props {
|
|
|
46
46
|
enableHaptics?: boolean
|
|
47
47
|
groupName?: string
|
|
48
48
|
helper?: string
|
|
49
|
+
inert?: boolean
|
|
49
50
|
isRequired?: boolean
|
|
50
51
|
label?: string
|
|
51
52
|
modelValue?: boolean
|
|
@@ -68,6 +69,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
68
69
|
autofocus: false,
|
|
69
70
|
isRequired: false,
|
|
70
71
|
tooltip: '',
|
|
72
|
+
inert: false,
|
|
71
73
|
})
|
|
72
74
|
|
|
73
75
|
const emit = defineEmits<Emits>()
|
|
@@ -80,6 +82,16 @@ const capitalizedColor = computed(() => {
|
|
|
80
82
|
return capitalizeFirstLetter(props.color)
|
|
81
83
|
})
|
|
82
84
|
|
|
85
|
+
const inertProps = computed(() => {
|
|
86
|
+
if (!props.inert) return
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
'aria-hidden': true,
|
|
90
|
+
tabindex: -1,
|
|
91
|
+
inert: true,
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
83
95
|
const computedClasses = computed(() => {
|
|
84
96
|
return [
|
|
85
97
|
{
|
package/src/components/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import CpAlert from './CpAlert.vue'
|
|
|
22
22
|
import CpBadge from './CpBadge.vue'
|
|
23
23
|
import CpButton from './CpButton.vue'
|
|
24
24
|
import CpButtonGroup from './CpButtonGroup.vue'
|
|
25
|
+
import CpButtonToggle from './CpButtonToggle.vue'
|
|
25
26
|
import CpCalendar from './CpCalendar.vue'
|
|
26
27
|
import CpCheckbox from './CpCheckbox.vue'
|
|
27
28
|
import CpContextualMenu from './CpContextualMenu.vue'
|
|
@@ -73,6 +74,7 @@ const Components = {
|
|
|
73
74
|
CpAccordion,
|
|
74
75
|
CpAccordionGroup,
|
|
75
76
|
CpToast,
|
|
77
|
+
CpButtonToggle,
|
|
76
78
|
CpBadge,
|
|
77
79
|
CpTabs,
|
|
78
80
|
CpHeading,
|
|
@@ -148,6 +150,7 @@ export {
|
|
|
148
150
|
CpAccordion,
|
|
149
151
|
CpAccordionGroup,
|
|
150
152
|
CpToast,
|
|
153
|
+
CpButtonToggle,
|
|
151
154
|
CpBadge,
|
|
152
155
|
CpTabs,
|
|
153
156
|
CpHeading,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
import type { Args, Meta, StoryObj } from '@storybook/vue3-vite'
|
|
4
|
+
|
|
5
|
+
import CpButtonToggle from '@/components/CpButtonToggle.vue'
|
|
6
|
+
import CpSwitch from '@/components/CpSwitch.vue'
|
|
7
|
+
|
|
8
|
+
import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/documentationStyles'
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
title: 'Atoms/CpButtonToggle',
|
|
12
|
+
component: CpButtonToggle,
|
|
13
|
+
argTypes: {
|
|
14
|
+
isSelected: {
|
|
15
|
+
control: 'boolean',
|
|
16
|
+
description: 'Whether the toggle button is selected.',
|
|
17
|
+
},
|
|
18
|
+
disabled: {
|
|
19
|
+
control: 'boolean',
|
|
20
|
+
description: 'Whether interactions are disabled.',
|
|
21
|
+
},
|
|
22
|
+
label: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Text displayed in the button.',
|
|
25
|
+
},
|
|
26
|
+
leadingIcon: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Icon displayed in the button.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
} satisfies Meta<typeof CpButtonToggle>
|
|
32
|
+
|
|
33
|
+
export default meta
|
|
34
|
+
type Story = StoryObj<typeof meta>
|
|
35
|
+
|
|
36
|
+
const defaultRender = (args: Args) => ({
|
|
37
|
+
components: { CpButtonToggle },
|
|
38
|
+
setup() {
|
|
39
|
+
return { args }
|
|
40
|
+
},
|
|
41
|
+
template: `
|
|
42
|
+
<CpButtonToggle v-bind="args" />
|
|
43
|
+
`,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
label: 'Title',
|
|
49
|
+
isSelected: false,
|
|
50
|
+
disabled: false,
|
|
51
|
+
leadingIcon: 'dashed-circle',
|
|
52
|
+
},
|
|
53
|
+
render: defaultRender,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const States: Story = {
|
|
57
|
+
parameters: { controls: { disable: true } },
|
|
58
|
+
render: () => ({
|
|
59
|
+
components: { CpButtonToggle },
|
|
60
|
+
setup() {
|
|
61
|
+
return { docCellStyle, docLabelStyle, docRowWrapStyle }
|
|
62
|
+
},
|
|
63
|
+
template: `
|
|
64
|
+
<div :style="docRowWrapStyle">
|
|
65
|
+
<div :style="docCellStyle">
|
|
66
|
+
<span :style="docLabelStyle">Default</span>
|
|
67
|
+
<CpButtonToggle label="Title" />
|
|
68
|
+
</div>
|
|
69
|
+
<div :style="docCellStyle">
|
|
70
|
+
<span :style="docLabelStyle">Selected</span>
|
|
71
|
+
<CpButtonToggle label="Title" :is-selected="true" />
|
|
72
|
+
</div>
|
|
73
|
+
<div :style="docCellStyle">
|
|
74
|
+
<span :style="docLabelStyle">Disabled</span>
|
|
75
|
+
<CpButtonToggle label="Title" :disabled="true" />
|
|
76
|
+
</div>
|
|
77
|
+
<div :style="docCellStyle">
|
|
78
|
+
<span :style="docLabelStyle">Disabled selected</span>
|
|
79
|
+
<CpButtonToggle label="Title" :is-selected="true" :disabled="true" />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
`,
|
|
83
|
+
}),
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const withInertSwitch: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
...Default.args,
|
|
89
|
+
isSelected: undefined,
|
|
90
|
+
},
|
|
91
|
+
render: (args: Args) => ({
|
|
92
|
+
components: { CpButtonToggle, CpSwitch },
|
|
93
|
+
setup() {
|
|
94
|
+
const isSelected = ref(false)
|
|
95
|
+
return { docCellStyle, docLabelStyle, docRowWrapStyle, isSelected, args }
|
|
96
|
+
},
|
|
97
|
+
template: `
|
|
98
|
+
<div :style="docRowWrapStyle">
|
|
99
|
+
<div :style="docCellStyle">
|
|
100
|
+
<span :style="docLabelStyle">Selected</span>
|
|
101
|
+
<CpButtonToggle v-bind="args" :is-selected="isSelected" @click="isSelected = !isSelected">
|
|
102
|
+
<template #leading>
|
|
103
|
+
<CpSwitch v-model="isSelected" inert :disabled="args.disabled" />
|
|
104
|
+
</template>
|
|
105
|
+
</CpButtonToggle>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
`,
|
|
109
|
+
}),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const CustomLeadingSlot: Story = {
|
|
113
|
+
parameters: { controls: { disable: true } },
|
|
114
|
+
render: () => ({
|
|
115
|
+
components: { CpButtonToggle },
|
|
116
|
+
template: `
|
|
117
|
+
<div>
|
|
118
|
+
<CpButtonToggle label="Title" :is-selected="true">
|
|
119
|
+
<template #leading>
|
|
120
|
+
<span style="
|
|
121
|
+
width: 16px;
|
|
122
|
+
height: 16px;
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
border-radius: 4px;
|
|
127
|
+
background: var(--cp-background-accent-solid);
|
|
128
|
+
color: var(--cp-foreground-white);
|
|
129
|
+
font-size: 11px;
|
|
130
|
+
line-height: 1;
|
|
131
|
+
">✓</span>
|
|
132
|
+
</template>
|
|
133
|
+
</CpButtonToggle>
|
|
134
|
+
</div>
|
|
135
|
+
`,
|
|
136
|
+
}),
|
|
137
|
+
}
|