@citizenplane/pimp 8.32.5 → 9.0.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.
@@ -11,20 +11,28 @@
11
11
  :autofocus="autofocus"
12
12
  @change="handleClick(modelValue)"
13
13
  />
14
- <span class="cpSwitch__icon">
15
- <cp-icon type="check" />
16
- </span>
17
14
  <span class="cpSwitch__knobContainer">
18
15
  <span class="cpSwitch__knob" />
19
16
  </span>
20
17
  </span>
21
- <span v-if="label" class="cpSwitch__label">{{ label }}</span>
18
+ <div class="cpSwitch__content">
19
+ <div class="cpSwitch__labelContainer">
20
+ <span v-if="label" class="cpSwitch__label">{{ label }}</span>
21
+ <span v-if="isRequired" class="u-asterisk">*</span>
22
+ <cp-tooltip v-if="tooltip" :content="tooltip">
23
+ <cp-icon type="tooltip" size="16" class="cpSwitch__tooltip" />
24
+ </cp-tooltip>
25
+ </div>
26
+ <span v-if="helper" class="cpSwitch__helper">{{ helper }}</span>
27
+ </div>
22
28
  </label>
23
29
  </template>
24
30
 
25
31
  <script setup lang="ts">
26
32
  import { ref, computed } from 'vue'
27
33
 
34
+ import CpTooltip from '@/components/CpTooltip.vue'
35
+
28
36
  import { ToggleColors } from '@/constants'
29
37
  import { capitalizeFirstLetter, randomString } from '@/helpers'
30
38
 
@@ -33,9 +41,12 @@ interface Props {
33
41
  color?: string
34
42
  disabled?: boolean
35
43
  groupName?: string
44
+ helper?: string
45
+ isRequired?: boolean
36
46
  label?: string
37
47
  modelValue?: boolean
38
48
  reverseLabel?: boolean
49
+ tooltip?: string
39
50
  }
40
51
 
41
52
  interface Emits {
@@ -45,11 +56,14 @@ interface Emits {
45
56
  const props = withDefaults(defineProps<Props>(), {
46
57
  modelValue: false,
47
58
  label: '',
59
+ helper: '',
48
60
  disabled: false,
49
61
  groupName: '',
50
- color: ToggleColors.BLUE,
62
+ color: ToggleColors.PURPLE,
51
63
  reverseLabel: false,
52
64
  autofocus: false,
65
+ isRequired: false,
66
+ tooltip: '',
53
67
  })
54
68
 
55
69
  const emit = defineEmits<Emits>()
@@ -66,6 +80,7 @@ const computedClasses = computed(() => {
66
80
  return [
67
81
  {
68
82
  'cpSwitch--hasLabel': props.label,
83
+ 'cpSwitch--hasHelper': props.helper,
69
84
  'cpSwitch--isActive': props.modelValue,
70
85
  'cpSwitch--isDisabled': props.disabled,
71
86
  'cpSwitch--isReversed': props.reverseLabel,
@@ -86,25 +101,21 @@ const handleClick = (value: boolean): void => {
86
101
  }
87
102
 
88
103
  &--is#{$className} &__switch:focus-within {
89
- box-shadow: 0 0 0 fn.px-to-em(3) color.scale($color, $lightness: 70%);
104
+ outline: $color solid fn.px-to-rem(3);
105
+ outline-offset: fn.px-to-rem(3);
90
106
  }
91
107
 
92
108
  &--is#{$className}:hover:not(#{&}--isDisabled) &__switch {
93
- background-color: color.scale($color, $lightness: 50%);
109
+ background-color: colors.$neutral-dark-1;
94
110
  }
95
111
 
96
112
  &--is#{$className}#{&}--isActive:hover &__switch {
97
- background-color: color.adjust($color, $lightness: -10%);
98
- }
99
-
100
- &--is#{$className}#{&}--isActive#{&}--isDisabled &__switch,
101
- &--is#{$className}#{&}--isActive#{&}--isDisabled:hover &__swtich {
102
- background-color: color.scale($color, $lightness: 65%);
113
+ background-color: colors.$purple-600;
103
114
  }
104
115
  }
105
116
 
106
117
  .cpSwitch {
107
- $cp-switch-base-height: fn.px-to-em(24);
118
+ $cp-switch-base-height: fn.px-to-em(20);
108
119
  align-items: center;
109
120
 
110
121
  &,
@@ -112,11 +123,32 @@ const handleClick = (value: boolean): void => {
112
123
  cursor: pointer;
113
124
  }
114
125
 
126
+ &__content {
127
+ display: flex;
128
+ flex-direction: column;
129
+ gap: fn.px-to-rem(4);
130
+ }
131
+
132
+ &__helper {
133
+ color: colors.$neutral-dark-1;
134
+ }
135
+
136
+ &__labelContainer {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: sp.$space-sm;
140
+ }
141
+
142
+ &__tooltip {
143
+ color: colors.$neutral-dark-3;
144
+ padding: sp.$space-sm;
145
+ }
146
+
115
147
  &--hasLabel {
116
148
  display: grid;
117
149
  grid-template-columns: min-content 1fr;
118
150
  grid-gap: fn.px-to-rem(12);
119
- align-items: center;
151
+ align-items: flex-start;
120
152
  }
121
153
 
122
154
  &--isDisabled,
@@ -134,7 +166,7 @@ const handleClick = (value: boolean): void => {
134
166
  &--isReversed {
135
167
  grid-template-columns: 1fr min-content;
136
168
 
137
- .cpSwitch__label {
169
+ .cpSwitch__content {
138
170
  grid-row: 1;
139
171
  }
140
172
  }
@@ -149,32 +181,22 @@ const handleClick = (value: boolean): void => {
149
181
  }
150
182
 
151
183
  &--isActive .cpSwitch {
152
- &__icon {
153
- transform: translateY(-50%);
154
- opacity: 1;
155
- }
156
-
157
184
  &__knobContainer {
158
185
  transform: translateX(50%);
159
186
  }
160
187
  }
161
188
 
162
189
  @include cp-switch-style(colors.$primary-color, 'Purple');
163
- @include cp-switch-style(colors.$secondary-color, 'Blue');
164
190
 
165
191
  &__switch {
166
192
  position: relative;
167
193
  border-radius: 1000px;
168
- background-color: colors.$neutral-dark-1;
194
+ background-color: colors.$gray-3;
169
195
  overflow: hidden;
170
196
  transition: background-color 0.1s ease-out;
171
197
  display: flex;
172
198
  height: $cp-switch-base-height;
173
199
  width: $cp-switch-base-height * 2;
174
-
175
- &:hover {
176
- background-color: colors.$neutral-dark-3;
177
- }
178
200
  }
179
201
 
180
202
  input {
@@ -187,31 +209,6 @@ const handleClick = (value: boolean): void => {
187
209
  height: 100%;
188
210
  }
189
211
 
190
- &__icon {
191
- position: absolute;
192
- left: 0;
193
- top: 50%;
194
- transform: translateY(-50%);
195
- opacity: 0;
196
- padding-left: fn.px-to-rem(4);
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- transition: opacity 0.12s ease-in;
201
- width: $cp-switch-base-height;
202
- height: $cp-switch-base-height;
203
-
204
- i {
205
- width: 80%;
206
- height: 100%;
207
- }
208
-
209
- svg {
210
- stroke-width: 3;
211
- color: colors.$neutral-light;
212
- }
213
- }
214
-
215
212
  &__knobContainer {
216
213
  width: 100%;
217
214
  padding: fn.px-to-rem(2);
@@ -101,11 +101,11 @@ export const WithIcons: Story = {
101
101
  setup: () => ({ args }),
102
102
  template: `
103
103
  <CpButton v-bind="args">
104
- <template #icon-before>
104
+ <template #leading-icon>
105
105
  <CpIcon type="arrow-left" />
106
106
  </template>
107
107
  Button with Icon
108
- <template #icon-after>
108
+ <template #trailing-icon>
109
109
  <CpIcon type="arrow-right" />
110
110
  </template>
111
111
  </CpButton>
@@ -8,13 +8,14 @@ const meta = {
8
8
  title: 'CpSwitch',
9
9
  component: CpSwitch,
10
10
  argTypes: {
11
- modelValue: {
11
+ autofocus: {
12
12
  control: 'boolean',
13
- description: 'The switch state',
13
+ description: 'Whether to autofocus the switch',
14
14
  },
15
- label: {
16
- control: 'text',
17
- description: 'Label text for the switch',
15
+ color: {
16
+ control: 'select',
17
+ options: ['blue', 'purple'],
18
+ description: 'Color variant of the switch',
18
19
  },
19
20
  disabled: {
20
21
  control: 'boolean',
@@ -24,18 +25,29 @@ const meta = {
24
25
  control: 'text',
25
26
  description: 'Name attribute for the switch',
26
27
  },
27
- color: {
28
- control: 'select',
29
- options: ['blue', 'purple'],
30
- description: 'Color variant of the switch',
28
+ helper: {
29
+ control: 'text',
30
+ description: 'Helper text to display below the label',
31
+ },
32
+ isRequired: {
33
+ control: 'boolean',
34
+ description: 'Whether the switch is required',
35
+ },
36
+ label: {
37
+ control: 'text',
38
+ description: 'Label text for the switch',
39
+ },
40
+ modelValue: {
41
+ control: 'boolean',
42
+ description: 'The switch state',
31
43
  },
32
44
  reverseLabel: {
33
45
  control: 'boolean',
34
46
  description: 'Whether to show label before the switch',
35
47
  },
36
- autofocus: {
37
- control: 'boolean',
38
- description: 'Whether to autofocus the switch',
48
+ tooltip: {
49
+ control: 'text',
50
+ description: 'Tooltip text to display',
39
51
  },
40
52
  },
41
53
  } satisfies Meta<typeof CpSwitch>
@@ -45,11 +57,14 @@ type Story = StoryObj<typeof meta>
45
57
 
46
58
  export const Default: Story = {
47
59
  args: {
48
- label: 'Switch Label',
60
+ label: 'Label',
61
+ helper: 'This is a helper text that provides additional information',
49
62
  disabled: false,
50
63
  color: 'purple',
51
64
  reverseLabel: false,
52
65
  autofocus: false,
66
+ isRequired: true,
67
+ tooltip: 'This is a tooltip text',
53
68
  },
54
69
  render: (args) => ({
55
70
  components: { CpSwitch },
@@ -83,6 +98,13 @@ export const DisabledChecked: Story = {
83
98
  },
84
99
  }
85
100
 
101
+ export const Required: Story = {
102
+ args: {
103
+ ...Default.args,
104
+ isRequired: true,
105
+ },
106
+ }
107
+
86
108
  export const Reversed: Story = {
87
109
  args: {
88
110
  ...Default.args,
@@ -105,6 +127,20 @@ export const Reversed: Story = {
105
127
  }),
106
128
  }
107
129
 
130
+ export const WithHelper: Story = {
131
+ args: {
132
+ ...Default.args,
133
+ helper: 'This is a helper text that provides additional information',
134
+ },
135
+ }
136
+
137
+ export const WithTooltip: Story = {
138
+ args: {
139
+ ...Default.args,
140
+ tooltip: 'This is a tooltip text',
141
+ },
142
+ }
143
+
108
144
  export const SwitchGroup: Story = {
109
145
  render: () => ({
110
146
  components: { CpSwitch },
@@ -132,6 +168,12 @@ export const SwitchGroup: Story = {
132
168
  v-model="switches.switch3"
133
169
  label="Third Switch"
134
170
  group-name="switch-group"
171
+ disabled
172
+ />
173
+ <CpSwitch
174
+ v-model="switches.switch4"
175
+ label="Fourth Switch"
176
+ group-name="switch-group"
135
177
  />
136
178
  </div>
137
179
  `,