@citizenplane/pimp 15.1.4 → 16.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "15.1.4",
3
+ "version": "16.0.1",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8081",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -98,8 +98,6 @@
98
98
  --cp-colors-accent-900: #1f0067;
99
99
  --cp-colors-accent-1000: #13004a;
100
100
 
101
- --cp-colors-white: #ffffff;
102
-
103
101
  --cp-colors-yellow-50: #fefeea;
104
102
  --cp-colors-yellow-100: #fcfacc;
105
103
  --cp-colors-yellow-200: #f0df80;
@@ -1,147 +1,256 @@
1
1
  <template>
2
- <div v-if="isDisplayed" class="cpAlert" :class="`cpAlert--${intent}`">
2
+ <div class="cpAlert" :class="dynamicClasses">
3
3
  <div class="cpAlert__icon">
4
- <div v-if="hasIconSlot" class="cpAlert__iconWrapper">
5
- <slot name="icon" />
6
- </div>
7
- <cp-icon v-else :type="alertIcon" />
4
+ <slot name="icon">
5
+ <cp-icon size="16" :type="alertIcon" />
6
+ </slot>
8
7
  </div>
9
8
  <div class="cpAlert__body">
10
- <cp-heading v-if="title" class="cpAlert__title" heading-level="h4" :size="400">{{ title }}</cp-heading>
11
- <div v-if="hasDefaultSlot" class="cpAlert__content">
12
- <slot />
9
+ <div class="cpAlert__content">
10
+ <p v-if="hasTitle" class="cpAlert__title">
11
+ <slot name="title">
12
+ {{ title }}
13
+ </slot>
14
+ </p>
15
+ <p v-if="hasContent" class="cpAlert__content">
16
+ <slot>
17
+ {{ content }}
18
+ </slot>
19
+ </p>
20
+ </div>
21
+ <div v-if="hasActions" class="cpAlert__actions">
22
+ <div v-if="hasPrimaryAction" class="cpAlert__action">
23
+ <slot name="primary-action">
24
+ <cp-button
25
+ :appearance="primaryActionAppearance"
26
+ :color="color"
27
+ is-square
28
+ :size="actionsSize"
29
+ @click="emit('primaryActionClick')"
30
+ >
31
+ {{ primaryActionLabel }}
32
+ </cp-button>
33
+ </slot>
34
+ </div>
35
+ <div v-if="hasSecondaryAction" class="cpAlert__action">
36
+ <slot name="secondary-action">
37
+ <cp-button
38
+ :appearance="secondaryActionAppearance"
39
+ :color="color"
40
+ is-square
41
+ :size="actionsSize"
42
+ @click="emit('secondaryActionClick')"
43
+ >
44
+ {{ secondaryActionLabel }}
45
+ </cp-button>
46
+ </slot>
47
+ </div>
13
48
  </div>
14
49
  </div>
15
- <button v-if="isClosable" class="cpAlert__button" type="button" @click="dismissAlert"><cp-icon type="x" /></button>
50
+ <div v-if="isClosable" class="cpAlert__close">
51
+ <cp-button appearance="tertiary" :color="color" is-square size="xs" @click="emit('onClose')">
52
+ <template #leading-icon>
53
+ <cp-icon size="16" type="x" />
54
+ </template>
55
+ </cp-button>
56
+ </div>
16
57
  </div>
17
58
  </template>
18
59
 
19
60
  <script setup lang="ts">
20
- import { ref, computed, useSlots } from 'vue'
61
+ import { computed, useSlots } from 'vue'
21
62
 
22
- import { Intent } from '@/constants'
63
+ import type { Colors } from '@/constants'
64
+ import { capitalizeFirstLetter } from '@/helpers'
65
+
66
+ type AlertColors = Extract<Colors, 'neutral' | 'accent' | 'success' | 'warning' | 'error'>
67
+ type AlertTypes = 'expanded' | 'inline'
23
68
 
24
69
  interface Props {
25
- intent: (typeof Intent)[keyof typeof Intent]['value']
70
+ color?: AlertColors
71
+ content?: string
72
+ icon?: string
26
73
  isClosable?: boolean
74
+ primaryActionLabel?: string
75
+ secondaryActionLabel?: string
27
76
  title?: string
77
+ type?: AlertTypes
28
78
  }
29
79
 
30
80
  const props = withDefaults(defineProps<Props>(), {
31
- title: '',
81
+ color: 'neutral',
82
+ icon: undefined,
83
+ content: undefined,
84
+ title: undefined,
32
85
  isClosable: false,
86
+ type: 'expanded',
87
+ primaryActionLabel: undefined,
88
+ secondaryActionLabel: undefined,
33
89
  })
34
90
 
91
+ const emit = defineEmits<{
92
+ onClose: []
93
+ primaryActionClick: []
94
+ secondaryActionClick: []
95
+ }>()
96
+
35
97
  const slots = useSlots()
36
98
 
37
- const isDisplayed = ref(true)
99
+ const colorProps = {
100
+ neutral: {
101
+ icon: 'dashed-circle',
102
+ primaryActionAppearance: 'secondary',
103
+ secondaryActionAppearance: 'tertiary',
104
+ },
105
+ accent: {
106
+ icon: 'info',
107
+ primaryActionAppearance: 'primary',
108
+ secondaryActionAppearance: 'secondary',
109
+ },
110
+ success: {
111
+ icon: 'check',
112
+ primaryActionAppearance: 'primary',
113
+ secondaryActionAppearance: 'secondary',
114
+ },
115
+ warning: {
116
+ icon: 'alert-triangle',
117
+ primaryActionAppearance: 'primary',
118
+ secondaryActionAppearance: 'secondary',
119
+ },
120
+ error: {
121
+ icon: 'x-octagon',
122
+ primaryActionAppearance: 'primary',
123
+ secondaryActionAppearance: 'secondary',
124
+ },
125
+ }
38
126
 
39
127
  const alertIcon = computed(() => {
40
- const intentValues = Object.values(Intent)
41
- return intentValues.find((intentItem) => intentItem.value === props.intent)?.icon
128
+ if (props.icon) return props.icon
129
+ return colorProps[props.color].icon
42
130
  })
43
131
 
44
- const hasIconSlot = computed(() => !!slots.icon)
45
- const hasDefaultSlot = computed(() => !!slots.default)
132
+ const isExpanded = computed(() => props.type === 'expanded')
133
+ const hasTitle = computed(() => !!props.title || !!slots.title)
134
+ const hasContent = computed(() => isExpanded.value && (!!props.content || !!slots.default))
135
+ const hasActions = computed(() => hasPrimaryAction.value || hasSecondaryAction.value)
136
+ const hasPrimaryAction = computed(() => !!props.primaryActionLabel || !!slots['primary-action'])
137
+ const hasSecondaryAction = computed(() => !!props.secondaryActionLabel || !!slots['secondary-action'])
138
+ const primaryActionAppearance = computed(() => colorProps[props.color].primaryActionAppearance)
139
+ const secondaryActionAppearance = computed(() => colorProps[props.color].secondaryActionAppearance)
140
+ const actionsSize = computed(() => (isExpanded.value ? 'sm' : 'xs'))
46
141
 
47
- const dismissAlert = () => (isDisplayed.value = false)
142
+ const dynamicClasses = computed(() => [
143
+ `cpAlert--is${capitalizeFirstLetter(props.type)}`,
144
+ `cpAlert--is${capitalizeFirstLetter(props.color)}`,
145
+ ])
48
146
  </script>
49
147
 
50
148
  <style lang="scss">
51
- @mixin cp-alert-style($bgColor, $textColor, $className) {
52
- &--#{$className} {
53
- background-color: $bgColor;
54
- color: $textColor;
55
-
56
- &::before {
57
- background-color: $textColor;
58
- }
59
- }
60
-
61
- &--#{$className} button:focus-visible {
62
- outline: fn.px-to-rem(2) solid $textColor;
63
- }
64
- }
65
-
66
149
  .cpAlert {
67
150
  position: relative;
68
151
  display: flex;
69
152
  align-items: flex-start;
70
- padding: fn.px-to-rem(10);
71
- font-size: fn.px-to-em(14);
72
- border-radius: fn.px-to-rem(4);
73
153
  overflow: hidden;
154
+ padding: var(--cp-spacing-lg) var(--cp-spacing-lg) var(--cp-spacing-lg) var(--cp-spacing-md);
155
+ gap: var(--cp-spacing-md);
156
+ width: 100%;
157
+ background-color: var(--cp-background-primary);
158
+ border: 1px solid var(--cp-alert-border);
159
+ border-radius: var(--cp-radius-md);
160
+ margin: var(--cp-spacing-sm);
161
+ box-shadow:
162
+ var(--cp-drop-shadow-3xs-offset-x) var(--cp-drop-shadow-3xs-offset-y) var(--cp-drop-shadow-3xs-blur)
163
+ fn.px-to-rem(-4) var(--cp-drop-shadow-3xs-color),
164
+ 0 0 0 var(--cp-dimensions-1) var(--cp-alert-shadow);
74
165
 
75
- &:before {
76
- content: '';
77
- position: absolute;
78
- left: 0;
79
- top: 0;
80
- height: 100%;
81
- width: fn.px-to-rem(3);
166
+ &__icon {
167
+ width: var(--cp-dimensions-4);
168
+ height: var(--cp-dimensions-4);
169
+ margin: var(--cp-spacing-sm-md) var(--cp-spacing-sm);
170
+ color: var(--cp-alert-icon);
82
171
  }
83
172
 
84
173
  &__body {
85
174
  flex: 1;
86
- margin-left: var(--cp-spacing-md);
87
- padding-right: calc(#{fn.px-to-rem(18)} + #{var(--cp-spacing-lg)});
175
+ display: flex;
176
+ gap: var(--cp-spacing-md);
177
+ flex-direction: column;
88
178
  }
89
179
 
90
- &__iconWrapper {
180
+ &__content {
91
181
  display: flex;
92
- padding-block: var(--cp-spacing-xs);
182
+ flex: 1;
183
+ flex-direction: column;
184
+ padding: var(--cp-spacing-sm) 0;
185
+ gap: var(--cp-spacing-sm);
93
186
  }
94
187
 
95
- &__icon {
96
- flex-shrink: 0;
97
- color: inherit;
98
-
99
- i {
100
- display: inline-block;
101
- vertical-align: sub;
102
- width: fn.px-to-rem(16);
103
- height: fn.px-to-rem(16);
104
- }
188
+ &__actions {
189
+ display: flex;
190
+ gap: var(--cp-spacing-md);
191
+ }
192
+
193
+ &__title {
194
+ font-size: var(--cp-text-size-sm);
195
+ line-height: var(--cp-line-height-sm);
196
+ font-weight: 600;
197
+ color: var(--cp-alert-title);
105
198
  }
106
199
 
107
- &__icon,
108
200
  &__content {
109
- line-height: fn.px-to-rem(19);
201
+ font-size: var(--cp-text-size-sm);
202
+ line-height: var(--cp-line-height-sm);
203
+ font-weight: 500;
204
+ color: var(--cp-alert-text);
110
205
  }
111
206
 
112
- &__body > .cpHeading {
113
- line-height: fn.px-to-rem(19);
114
- color: inherit;
115
- font-weight: 600;
207
+ &--isInline {
208
+ align-items: center;
116
209
 
117
- &:not(:only-child) {
118
- margin-bottom: var(--cp-spacing-sm);
210
+ .cpAlert__body {
211
+ flex-direction: row;
212
+ align-items: center;
119
213
  }
120
214
  }
121
215
 
122
- &__button {
123
- display: flex;
124
- position: absolute;
125
- right: fn.px-to-rem(7);
126
- top: fn.px-to-rem(7);
127
- border-radius: fn.px-to-rem(4);
128
- padding: var(--cp-spacing-sm);
129
- color: inherit;
130
-
131
- svg {
132
- margin: 0;
133
- width: fn.px-to-rem(18);
134
- height: fn.px-to-rem(18);
135
- }
216
+ &--isNeutral {
217
+ --cp-alert-border: var(--cp-border-soft);
218
+ --cp-alert-shadow: var(--cp-utility-neutral-100);
219
+ --cp-alert-icon: var(--cp-foreground-primary);
220
+ --cp-alert-title: var(--cp-text-primary);
221
+ --cp-alert-text: var(--cp-text-secondary);
222
+ }
136
223
 
137
- &:hover {
138
- background-color: inherit;
139
- }
224
+ &--isAccent {
225
+ --cp-alert-border: var(--cp-border-accent-primary);
226
+ --cp-alert-shadow: var(--cp-utility-accent-100);
227
+ --cp-alert-icon: var(--cp-foreground-accent-secondary);
228
+ --cp-alert-title: var(--cp-text-accent-primary);
229
+ --cp-alert-text: var(--cp-text-accent-secondary);
230
+ }
231
+
232
+ &--isSuccess {
233
+ --cp-alert-border: var(--cp-border-success-primary);
234
+ --cp-alert-shadow: var(--cp-utility-success-100);
235
+ --cp-alert-icon: var(--cp-foreground-success-secondary);
236
+ --cp-alert-title: var(--cp-text-success-primary);
237
+ --cp-alert-text: var(--cp-text-success-secondary);
238
+ }
239
+
240
+ &--isWarning {
241
+ --cp-alert-border: var(--cp-border-warning-primary);
242
+ --cp-alert-shadow: var(--cp-utility-warning-100);
243
+ --cp-alert-icon: var(--cp-foreground-warning-secondary);
244
+ --cp-alert-title: var(--cp-text-warning-primary);
245
+ --cp-alert-text: var(--cp-text-warning-secondary);
140
246
  }
141
247
 
142
- @include cp-alert-style(var(--cp-background-accent-secondary), var(--cp-text-accent-primary), 'info');
143
- @include cp-alert-style(var(--cp-background-success-secondary), var(--cp-text-success-primary), 'success');
144
- @include cp-alert-style(var(--cp-background-warning-secondary), var(--cp-text-warning-primary), 'warning');
145
- @include cp-alert-style(var(--cp-background-error-secondary), var(--cp-text-error-primary), 'critical');
248
+ &--isError {
249
+ --cp-alert-border: var(--cp-border-error-primary);
250
+ --cp-alert-shadow: var(--cp-utility-error-100);
251
+ --cp-alert-icon: var(--cp-foreground-error-secondary);
252
+ --cp-alert-title: var(--cp-text-error-primary);
253
+ --cp-alert-text: var(--cp-text-error-secondary);
254
+ }
146
255
  }
147
256
  </style>
@@ -124,13 +124,16 @@ const handleClick = () => {
124
124
  &--isSecondary#{&}--is#{$className} {
125
125
  &:not(:disabled) {
126
126
  background-color: var(--cp-background-primary);
127
- border: fn.px-to-rem(1) solid var(--cp-border-soft);
128
127
  color: $color;
129
- box-shadow: var(--cp-shadows-3xs);
128
+ box-shadow:
129
+ var(--cp-shadows-3xs-inset),
130
+ 0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);
130
131
 
131
132
  &:hover {
132
133
  background-color: var(--cp-background-primary-hover);
133
- border-color: var(--cp-border-soft-hover);
134
+ box-shadow:
135
+ var(--cp-shadows-3xs-inset),
136
+ 0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);
134
137
  }
135
138
 
136
139
  &:focus,
@@ -7,6 +7,10 @@
7
7
  <script setup lang="ts">
8
8
  import { HeadingLevels } from '@/constants'
9
9
 
10
+ /**
11
+ * @deprecated This component is deprecated. Please use directly CSS variables instead.
12
+ */
13
+
10
14
  type HeadingSize = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
11
15
 
12
16
  interface Props {
@@ -65,7 +65,7 @@ const getTabClass = (tabIndex: number) => {
65
65
  return [{ 'cpTabs__tab--isActive': activeTabIndex.value === tabIndex }, { 'cpTabs__tab--isLoading': props.isLoading }]
66
66
  }
67
67
 
68
- const dynamicBadgeColor = (index: number) => (index === activeTabIndex.value ? 'purple' : 'gray')
68
+ const dynamicBadgeColor = (index: number) => (index === activeTabIndex.value ? 'accent' : 'neutral')
69
69
 
70
70
  const getActiveTabElement = () => {
71
71
  const componentElement = cpTabsElement.value
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
3
+ <path
4
+ fill-rule="evenodd"
5
+ clip-rule="evenodd"
6
+ d="M9.9169 1.19704C10.4941 1.0864 11 1.55136 11 2.13907V2.15944C11 2.66921 10.6148 3.09135 10.1162 3.19753C9.02753 3.42938 8.0117 3.85808 7.10775 4.4446C6.68031 4.72194 6.10975 4.6956 5.74945 4.3353L5.73512 4.32098C5.31937 3.90522 5.34858 3.21846 5.83525 2.88854C7.0455 2.0681 8.42786 1.48246 9.9169 1.19704Z"
7
+ />
8
+ <path
9
+ fill-rule="evenodd"
10
+ clip-rule="evenodd"
11
+ d="M19.6648 18.2505C19.3045 17.8903 19.2781 17.3197 19.5555 16.8922C20.142 15.9883 20.5707 14.9725 20.8025 13.8838C20.9087 13.3852 21.3308 13 21.8406 13H21.861C22.4487 13 22.9137 13.5059 22.803 14.0831C22.5176 15.5722 21.932 16.9545 21.1115 18.1647C20.7816 18.6514 20.0948 18.6806 19.6791 18.2649L19.6648 18.2505Z"
12
+ />
13
+ <path
14
+ fill-rule="evenodd"
15
+ clip-rule="evenodd"
16
+ d="M22.803 9.91695C22.9137 10.4942 22.4487 11.0001 21.861 11.0001H21.8406C21.3309 11.0001 20.9087 10.6149 20.8025 10.1163C20.5707 9.02758 20.142 8.01174 19.5554 7.10779C19.2781 6.68035 19.3044 6.10978 19.6647 5.74948L19.6791 5.73515C20.0948 5.3194 20.7816 5.34861 21.1115 5.83528C21.9319 7.04553 22.5176 8.4279 22.803 9.91695Z"
17
+ />
18
+ <path
19
+ fill-rule="evenodd"
20
+ clip-rule="evenodd"
21
+ d="M18.2505 19.6649C17.8903 19.3046 17.3197 19.2782 16.8922 19.5556C15.9883 20.1421 14.9725 20.5708 13.8838 20.8026C13.3853 20.9088 13 21.331 13 21.8407V21.8611C13 22.4488 13.506 22.9138 14.0832 22.8031C15.5722 22.5177 16.9545 21.9321 18.1647 21.1116C18.6514 20.7817 18.6806 20.095 18.2649 19.6792L18.2505 19.6649Z"
22
+ />
23
+ <path
24
+ fill-rule="evenodd"
25
+ clip-rule="evenodd"
26
+ d="M9.91689 22.8029C10.4941 22.9136 11 22.4486 11 21.8609V21.8405C11 21.3308 10.6148 20.9086 10.1162 20.8024C9.02751 20.5706 8.01166 20.1419 7.1077 19.5553C6.68026 19.278 6.10969 19.3043 5.74939 19.6646L5.73506 19.679C5.31931 20.0947 5.34851 20.7815 5.83519 21.1114C7.04545 21.9319 8.42783 22.5175 9.91689 22.8029Z"
27
+ />
28
+ <path
29
+ fill-rule="evenodd"
30
+ clip-rule="evenodd"
31
+ d="M13 2.15944C13 2.66921 13.3853 3.09134 13.8838 3.19753C14.9725 3.42938 15.9883 3.85806 16.8922 4.44456C17.3196 4.7219 17.8902 4.69555 18.2505 4.33526L18.2648 4.32093C18.6806 3.90517 18.6514 3.21841 18.1647 2.88849C16.9545 2.06808 15.5722 1.48246 14.0832 1.19704C13.506 1.0864 13 1.55135 13 2.13907V2.15944Z"
32
+ />
33
+ <path
34
+ fill-rule="evenodd"
35
+ clip-rule="evenodd"
36
+ d="M4.32091 5.73515C3.90516 5.3194 3.2184 5.34861 2.88847 5.83528C2.06804 7.04552 1.4824 8.42787 1.19698 9.9169C1.08634 10.4941 1.55129 11 2.13901 11H2.15938C2.66915 11 3.09128 10.6148 3.19747 10.1162C3.42932 9.02755 3.85802 8.01173 4.44454 7.10778C4.72188 6.68034 4.69553 6.10978 4.33524 5.74948L4.32091 5.73515Z"
37
+ />
38
+ <path
39
+ fill-rule="evenodd"
40
+ clip-rule="evenodd"
41
+ d="M2.13901 13C1.55129 13 1.08634 13.5059 1.19698 14.0831C1.48239 15.5721 2.06802 16.9545 2.88843 18.1647C3.21835 18.6514 3.90511 18.6806 4.32087 18.2648L4.3352 18.2505C4.69549 17.8902 4.72184 17.3196 4.4445 16.8922C3.858 15.9883 3.42931 14.9725 3.19747 13.8838C3.09128 13.3852 2.66915 13 2.15938 13H2.13901Z"
42
+ />
43
+ </svg>
44
+ </template>
@@ -34,6 +34,7 @@ import IconCollapseAlt from '@/components/icons/IconCollapseAlt.vue'
34
34
  import IconConsolidator from '@/components/icons/IconConsolidator.vue'
35
35
  import IconContact from '@/components/icons/IconContact.vue'
36
36
  import IconCorrosive from '@/components/icons/IconCorrosive.vue'
37
+ import IconDashedCircle from '@/components/icons/IconDashedCircle.vue'
37
38
  import IconDedicated from '@/components/icons/IconDedicated.vue'
38
39
  import IconDeparture from '@/components/icons/IconDeparture.vue'
39
40
  import IconDeviceForbidden from '@/components/icons/IconDeviceForbidden.vue'
@@ -166,6 +167,7 @@ export const CustomCpIcons = {
166
167
  consolidator: IconConsolidator,
167
168
  contact: IconContact,
168
169
  corrosive: IconCorrosive,
170
+ 'dashed-circle': IconDashedCircle,
169
171
  dedicated: IconDedicated,
170
172
  departure: IconDeparture,
171
173
  'direct-flight-alt': IconDirectFlightAlt,