@citizenplane/pimp 7.0.1 → 8.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.
Files changed (42) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.lintstagedrc.json +2 -2
  3. package/README.md +18 -6
  4. package/dist/pimp.es.js +10669 -0
  5. package/dist/pimp.umd.js +9 -0
  6. package/dist/style.css +1 -0
  7. package/package-lock.json +2295 -1775
  8. package/package.json +27 -20
  9. package/src/App.vue +0 -1
  10. package/src/README.md +23 -9
  11. package/src/assets/styles/base/_base.scss +2 -15
  12. package/src/assets/styles/helpers/_functions.scss +1 -1
  13. package/src/assets/styles/helpers/_keyframes.scss +25 -0
  14. package/src/assets/styles/lib/_normalize.scss +19 -41
  15. package/src/assets/styles/variables/_colors.scss +16 -16
  16. package/src/assets/styles/variables/_sizing.scss +1 -1
  17. package/src/assets/styles/variables/_spacing.scss +2 -2
  18. package/src/components/atomic-elements/CpBadge.vue +41 -29
  19. package/src/components/buttons/CpButton.vue +1 -1
  20. package/src/components/core/BaseInputLabel/index.vue +0 -1
  21. package/src/components/core/playground-sections/SectionAtomicElements.vue +20 -4
  22. package/src/components/core/playground-sections/SectionDatePickers.vue +26 -1
  23. package/src/components/core/playground-sections/SectionSimpleInputs.vue +6 -1
  24. package/src/components/date-pickers/CpCalendar.vue +49 -2
  25. package/src/components/date-pickers/CpDate.vue +61 -39
  26. package/src/components/date-pickers/CpDatepicker.vue +50 -3
  27. package/src/components/feedback-indicators/CpAlert.vue +4 -4
  28. package/src/components/index.js +15 -0
  29. package/src/components/inputs/CpInput.vue +59 -75
  30. package/src/components/inputs/CpTextarea.vue +19 -2
  31. package/src/components/lists-and-table/CpTable/index.scss +15 -9
  32. package/src/components/selects/CpSelect.vue +21 -2
  33. package/src/components/toggles/CpCheckbox/index.scss +1 -1
  34. package/src/components/toggles/CpRadio/index.scss +2 -2
  35. package/src/components/visual/CpIcon.vue +9 -2
  36. package/src/libs/CoreDatepicker.vue +101 -118
  37. package/vite.config.js +19 -0
  38. package/dist/assets/chevron-down-icon.1e5b69a2.svg +0 -3
  39. package/dist/assets/index.2be314eb.css +0 -1
  40. package/dist/assets/index.e3e1e2b7.js +0 -1
  41. package/dist/assets/vendor.ed632372.js +0 -9
  42. package/dist/index.html +0 -20
@@ -5,7 +5,7 @@
5
5
  Default date input: {{ cpDate }}
6
6
  </cp-heading>
7
7
  <div class="sectionDatePickers__datepickers">
8
- <cp-date v-model="cpDate" label="Default date input" class="sectionDatePickers__date" />
8
+ <cp-date v-model="cpDate" label="Default date input" class="sectionDatePickers__date" :is-invalid="false" />
9
9
  </div>
10
10
  </div>
11
11
  <div class="sectionDatePickers__type">
@@ -16,6 +16,19 @@
16
16
  <cp-datepicker label="Simple datepicker" :init-date-one="date" @dates="setDate" />
17
17
  <cp-button class="sectionDatePickers__button" @click="resetDate">Reset date</cp-button>
18
18
  </div>
19
+ <div class="sectionDatePickers__datepickers">
20
+ <div class="sectionDatePickers__wrapper">
21
+ <cp-input id="datepicker-trigger" label="CoreDatepicker" placeholder="CoreDatepicker" />
22
+ <cp-core-datepicker
23
+ class="sectionDatePickers__custom"
24
+ mode="range"
25
+ close-after-select
26
+ is-inline
27
+ @opened="() => (isDepartureDatepickerOpen = true)"
28
+ @closed="() => (isDepartureDatepickerOpen = false)"
29
+ />
30
+ </div>
31
+ </div>
19
32
  </div>
20
33
  <div class="sectionDatePickers__type">
21
34
  <cp-heading heading-level="h3" :size="600" class="sectionDatePickers__title">
@@ -23,6 +36,7 @@
23
36
  </cp-heading>
24
37
  <div class="sectionDatePickers__datepickers">
25
38
  <cp-datepicker label="Range datepicker" single-month is-inline :init-date-one="date" @dates="setDate" />
39
+ <cp-datepicker label="Range datepicker" is-inline :init-date-one="date" @dates="setDate" />
26
40
  </div>
27
41
  </div>
28
42
  <div class="sectionDatePickers__type">
@@ -84,6 +98,7 @@ export default {
84
98
  return {
85
99
  cpDate: '',
86
100
  date: null,
101
+ isDepartureDatepickerOpen: false,
87
102
  }
88
103
  },
89
104
  methods: {
@@ -125,5 +140,15 @@ export default {
125
140
  &__button {
126
141
  margin-left: $space-lg;
127
142
  }
143
+
144
+ &__wrapper {
145
+ position: relative;
146
+ }
147
+
148
+ &__custom {
149
+ position: absolute;
150
+ top: calc(100% + 16px);
151
+ left: 0;
152
+ }
128
153
  }
129
154
  </style>
@@ -70,7 +70,12 @@
70
70
  <div class="sectionSimpleInputs__type">
71
71
  <cp-heading heading-level="h3" :size="600" class="sectionSimpleInputs__title">Disabled</cp-heading>
72
72
  <div class="sectionSimpleInputs__inputs">
73
- <cp-input label="Empty disabled input with icon before" type="text" placeholder="Input placeholder" disabled>
73
+ <cp-input
74
+ label="Empty disabled input with icon before"
75
+ type="text"
76
+ placeholder="Input placeholder"
77
+ :disabled="true"
78
+ >
74
79
  <template #input-icon><cp-icon type="map-pin" /></template>
75
80
  </cp-input>
76
81
  <cp-input
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div class="cpCalendar">
3
3
  <cp-input
4
+ :id="triggerElementId"
4
5
  type="text"
5
- :input-id="triggerElementId"
6
6
  :model-value="humanDateFormat(dateOne, dateTwo)"
7
7
  placeholder="Select a date"
8
8
  :is-invalid="isError"
@@ -20,6 +20,8 @@
20
20
  :close-after-select="closeAfterSelect"
21
21
  :close-calendar="triggerCalendar"
22
22
  :inline="isInline"
23
+ class="cpCalendar__datepicker"
24
+ :class="dynamicClasses"
23
25
  @date-one-selected="(date) => selectDate('dateOne', date)"
24
26
  @date-two-selected="(date) => selectDate('dateTwo', date)"
25
27
  @is-inline="(value) => (isInline = value)"
@@ -162,6 +164,9 @@ export default {
162
164
  isDaysDisplayed() {
163
165
  return this.mode === 'range'
164
166
  },
167
+ dynamicClasses() {
168
+ return { 'cpCalendar__datepicker--isInline': this.isInline }
169
+ },
165
170
  },
166
171
  watch: {
167
172
  isInline(value) {
@@ -253,7 +258,49 @@ export default {
253
258
  opacity: 0;
254
259
  }
255
260
  .cpCalendar {
256
- position: relative;
261
+ // Opening transition layout adaptation
262
+ .asd__fade-enter-from {
263
+ transform: translateX(-50%) scale(0.8);
264
+ }
265
+
266
+ .asd__fade-enter-to {
267
+ transform: translateX(-50%) scale(1);
268
+ }
269
+
270
+ &__datepicker {
271
+ position: absolute;
272
+ left: 50%;
273
+ transform: translateX(-50%);
274
+ }
275
+
276
+ &__datepicker:not(&__datepicker--isInline) {
277
+ margin-top: 10px;
278
+ }
279
+
280
+ @media (min-width: 350px) {
281
+ position: relative;
282
+ }
283
+
284
+ @media (max-width: 767px) and (min-width: 350px) {
285
+ &__datepicker {
286
+ left: 0;
287
+ right: 0;
288
+ transform: initial;
289
+ }
290
+
291
+ &__datepicker:not(&__datepicker--isInline) {
292
+ margin-top: 16px;
293
+ }
294
+
295
+ // Opening transition layout adaptation
296
+ .asd__fade-enter-from {
297
+ transform: scale(0.8);
298
+ }
299
+
300
+ .asd__fade-enter-to {
301
+ transform: scale(1);
302
+ }
303
+ }
257
304
 
258
305
  &__input input:disabled {
259
306
  background-color: $neutral-light;
@@ -5,22 +5,26 @@
5
5
  </label>
6
6
  <div class="cpDate__inputs">
7
7
  <input
8
- ref="day"
9
8
  v-model="day"
9
+ v-maska="'##'"
10
10
  placeholder="DD"
11
11
  class="cpDate__day"
12
- type="number"
13
12
  inputmode="numeric"
14
- :min="1"
15
- :max="31"
16
13
  maxlength="2"
17
14
  :required="required"
18
15
  :disabled="disabled"
16
+ :autocomplete="autocompleteFields.day"
19
17
  />
20
18
  <div class="cpDate__divider" />
21
19
  <div class="cpDate__month" :class="selectDynamicClass">
22
- <select :id="cpDateId" v-model="month" :required="required" :disabled="disabled">
23
- <option value disabled>Month</option>
20
+ <select
21
+ :id="cpDateId"
22
+ v-model="month"
23
+ :required="required"
24
+ :disabled="disabled"
25
+ :autocomplete="autocompleteFields.month"
26
+ >
27
+ <option value>Month</option>
24
28
  <option v-for="(monthItem, index) in months" :key="index" :value="monthItem.value">
25
29
  {{ monthItem.label }}
26
30
  </option>
@@ -28,21 +32,19 @@
28
32
  </div>
29
33
  <div class="cpDate__divider" />
30
34
  <input
31
- ref="year"
32
35
  v-model="year"
36
+ v-maska="'####'"
33
37
  placeholder="YYYY"
34
38
  class="cpDate__year"
35
- type="number"
36
39
  inputmode="numeric"
37
- :min="minYear"
38
- :max="maxYear"
39
40
  maxlength="4"
40
41
  :disabled="disabled"
41
42
  :required="required"
43
+ :autocomplete="autocompleteFields.year"
42
44
  />
43
45
  </div>
44
46
  <transition-expand>
45
- <div v-if="!isDateValid" class="cpDate__errorMessage">{{ errorMessage }}</div>
47
+ <div v-if="advancedErrorMessage" class="cpDate__errorMessage">{{ advancedErrorMessage }}</div>
46
48
  </transition-expand>
47
49
  </div>
48
50
  </template>
@@ -80,6 +82,18 @@ export default {
80
82
  type: Boolean,
81
83
  default: false,
82
84
  },
85
+ isInvalid: {
86
+ type: Boolean,
87
+ default: false,
88
+ },
89
+ errorMessage: {
90
+ type: String,
91
+ default: '',
92
+ },
93
+ autocompleteBirthday: {
94
+ type: Boolean,
95
+ default: false,
96
+ },
83
97
  },
84
98
  emits: ['update:modelValue', 'on-validation'],
85
99
  data() {
@@ -144,13 +158,16 @@ export default {
144
158
  return this.day === '' && this.month === '' && this.year === ''
145
159
  },
146
160
  isDateValid() {
147
- if (!this.required && this.areInputsEmpty) return true
161
+ if (this.areInputsEmpty && !this.errorMessage) return true
148
162
 
149
- const isValid =
150
- this.isDayValid && this.isMonthValid && this.isYearValid && this.isDateBeforeMaxDate && this.isDateAfterMinDate
151
- this.$emit('on-validation', isValid)
152
-
153
- return isValid
163
+ return (
164
+ !this.isInvalid &&
165
+ this.isDayValid &&
166
+ this.isMonthValid &&
167
+ this.isYearValid &&
168
+ this.isDateBeforeMaxDate &&
169
+ this.isDateAfterMinDate
170
+ )
154
171
  },
155
172
  isDayValid() {
156
173
  return this.day >= 1 && this.day <= this.monthMaxDay
@@ -164,10 +181,10 @@ export default {
164
181
  areAllFieldsEmpty() {
165
182
  return !this.day && !this.month && !this.year
166
183
  },
167
- errorMessage() {
168
- if (this.areAllFieldsEmpty && this.required) {
169
- return `The ${this.label} field is required.`
170
- }
184
+ advancedErrorMessage() {
185
+ if (this.isDateValid) return ''
186
+
187
+ if (this.errorMessage) return this.errorMessage
171
188
 
172
189
  if (!this.isMonthValid) {
173
190
  return 'Month is required.'
@@ -204,6 +221,15 @@ export default {
204
221
  'cpDate__month--isEmpty': !this.month,
205
222
  }
206
223
  },
224
+ autocompleteFields() {
225
+ if (!this.autocompleteBirthday) return 'off'
226
+
227
+ return {
228
+ day: 'bday-day',
229
+ month: 'bday-month',
230
+ year: 'bday-year',
231
+ }
232
+ },
207
233
  },
208
234
  watch: {
209
235
  day() {
@@ -211,7 +237,6 @@ export default {
211
237
  },
212
238
  month() {
213
239
  this.handleUpdate()
214
- this.focusOnFirstEmptyInput()
215
240
  },
216
241
  year() {
217
242
  this.handleUpdate()
@@ -224,17 +249,9 @@ export default {
224
249
  return DateTime.fromISO(this.modelValue)[token]
225
250
  },
226
251
  handleUpdate() {
227
- const dateValue = this.isDateValid ? this.isoDate : 'Invalid Datetime'
252
+ this.$emit('update:modelValue', this.isoDate)
228
253
 
229
- this.$emit('update:modelValue', dateValue)
230
- },
231
- focusOnFirstEmptyInput() {
232
- if (!this.isDayValid) {
233
- this.$refs.day.focus()
234
- return
235
- }
236
-
237
- if (!this.isYearValid) this.$refs.year.focus()
254
+ this.$emit('on-validation', this.isDateValid)
238
255
  },
239
256
  },
240
257
  }
@@ -273,14 +290,14 @@ export default {
273
290
  .cpDate {
274
291
  input::placeholder,
275
292
  &__month--isEmpty {
276
- color: $neutral-dark-1;
293
+ color: $neutral-dark;
277
294
  }
278
295
 
279
296
  &__label {
280
297
  display: flex;
281
298
  align-items: center;
282
- margin: pxToRem(6) 0;
283
- font-size: pxToRem(14);
299
+ margin-bottom: pxToEm(6);
300
+ font-size: pxToEm(14);
284
301
 
285
302
  svg {
286
303
  margin-left: $space-sm;
@@ -331,8 +348,8 @@ export default {
331
348
  position: absolute;
332
349
  top: 50%;
333
350
  right: pxToEm(12);
334
- width: pxToRem(20);
335
- height: pxToRem(20);
351
+ width: pxToEm(20);
352
+ height: pxToEm(20);
336
353
  background-image: url('@/assets/images/icons/chevron-down-icon.svg');
337
354
  background-size: cover;
338
355
  transform: translateY(-50%);
@@ -343,9 +360,14 @@ export default {
343
360
  select {
344
361
  @extend %u-text-ellipsis;
345
362
  width: 100%;
346
- padding: pxToRem(12) pxToRem(40) pxToRem(12) pxToRem(12);
363
+ padding: pxToEm(12) pxToEm(40) pxToEm(12) pxToEm(12);
347
364
  color: inherit;
348
365
  cursor: pointer;
366
+
367
+ &:focus-visible {
368
+ text-decoration: underline;
369
+ font-weight: 500;
370
+ }
349
371
  }
350
372
 
351
373
  select > option:not(:disabled) {
@@ -395,7 +417,7 @@ export default {
395
417
  margin-top: pxToRem(6);
396
418
  color: $error-color;
397
419
  font-weight: 500;
398
- font-size: pxToRem(14);
420
+ font-size: pxToEm(14);
399
421
  }
400
422
  }
401
423
  </style>
@@ -2,9 +2,9 @@
2
2
  <div class="cpDatepicker">
3
3
  <cp-input
4
4
  v-show="!isInline"
5
+ :id="datePickerReferenceId"
5
6
  :model-value="inputComputedValue"
6
7
  type="text"
7
- :input-id="datePickerReferenceId"
8
8
  :placeholder="placeholder"
9
9
  :is-invalid="isError"
10
10
  :error-message="errorMessage"
@@ -22,6 +22,8 @@
22
22
  :months-to-show="numberOfMonths"
23
23
  :min-date="computedMinDate"
24
24
  :max-date="maxDate"
25
+ class="cpDatepicker__datepicker"
26
+ :class="{ 'cpDatepicker__datepicker--isInline': isInline }"
25
27
  @date-one-selected="(date) => selectDate('dateOne', date)"
26
28
  @date-two-selected="(date) => selectDate('dateTwo', date)"
27
29
  @opened="() => (isDisabled = true)"
@@ -134,6 +136,9 @@ export default {
134
136
  computedMinDate() {
135
137
  return this.allowPastDates ? '' : this.minDate
136
138
  },
139
+ dynamicClasses() {
140
+ return { 'cpDatepicker__datepicker--isInline': this.isInline }
141
+ },
137
142
  },
138
143
  watch: {
139
144
  initDateOne(newValue, oldValue) {
@@ -146,7 +151,7 @@ export default {
146
151
  methods: {
147
152
  selectDate(dateType, dateValue) {
148
153
  this[dateType] = dateValue
149
- this.$emit('dates', [this.dateOne, ...(this.dateTwo && this.dateTwo)])
154
+ this.$emit('dates', [this.dateOne, ...(this.dateTwo && [this.dateTwo])])
150
155
  },
151
156
  updateDateValue(date, newValue, oldValue) {
152
157
  if (newValue !== oldValue) {
@@ -159,10 +164,52 @@ export default {
159
164
 
160
165
  <style lang="scss">
161
166
  .cpDatepicker {
162
- position: relative;
167
+ // Opening transition layout adaptation
168
+ .asd__fade-enter-from {
169
+ transform: translateX(-50%) scale(0.8);
170
+ }
171
+
172
+ .asd__fade-enter-to {
173
+ transform: translateX(-50%) scale(1);
174
+ }
163
175
 
164
176
  &__input input:disabled {
165
177
  background-color: $neutral-light;
166
178
  }
179
+
180
+ &__datepicker {
181
+ position: absolute;
182
+ left: 50%;
183
+ transform: translateX(-50%);
184
+ }
185
+
186
+ &__datepicker:not(&__datepicker--isInline) {
187
+ margin-top: 10px;
188
+ }
189
+
190
+ @media (min-width: 350px) {
191
+ position: relative;
192
+ }
193
+
194
+ @media (max-width: 767px) and (min-width: 350px) {
195
+ &__datepicker {
196
+ left: 0;
197
+ right: 0;
198
+ transform: initial;
199
+ }
200
+
201
+ &__datepicker:not(&__datepicker--isInline) {
202
+ margin-top: 16px;
203
+ }
204
+
205
+ // Opening transition layout adaptation
206
+ .asd__fade-enter-from {
207
+ transform: scale(0.8);
208
+ }
209
+
210
+ .asd__fade-enter-to {
211
+ transform: scale(1);
212
+ }
213
+ }
167
214
  }
168
215
  </style>
@@ -19,6 +19,7 @@ export default {
19
19
  intent: {
20
20
  type: String,
21
21
  required: true,
22
+ default: Intent.INFO.value,
22
23
  validator(value) {
23
24
  const intentValues = Object.values(Intent).map((item) => item.value)
24
25
  return intentValues.includes(value)
@@ -68,9 +69,8 @@ export default {
68
69
  }
69
70
  }
70
71
 
71
- &--is#{$className} &__button:focus {
72
- outline: none;
73
- box-shadow: 0 0 0 pxToEm(3) scale-color($color, $lightness: 70%);
72
+ &--#{$className} button:focus-visible {
73
+ outline: pxToRem(2) solid $color;
74
74
  }
75
75
  }
76
76
 
@@ -79,7 +79,7 @@ export default {
79
79
  display: flex;
80
80
  align-items: flex-start;
81
81
  padding: pxToRem(10);
82
- font-size: pxToRem(14);
82
+ font-size: pxToEm(14);
83
83
  border-radius: pxToRem(4);
84
84
  overflow: hidden;
85
85
 
@@ -1,3 +1,10 @@
1
+ // PLUGINS
2
+ import maska from 'maska'
3
+
4
+ // DIRECTIVES
5
+ import ClickOutside from '../directives/ClickOutside'
6
+
7
+ // COMPONENTS
1
8
  // Atomic elements
2
9
  import CpBadge from './atomic-elements/CpBadge.vue'
3
10
 
@@ -9,6 +16,7 @@ import CpButton from './buttons/CpButton.vue'
9
16
 
10
17
  // Date pickers
11
18
  import CpDate from './date-pickers/CpDate.vue'
19
+ import CpCoreDatepicker from '../libs/CoreDatepicker.vue'
12
20
  import CpDatepicker from './date-pickers/CpDatepicker.vue'
13
21
  import CpCalendar from './date-pickers/CpCalendar.vue'
14
22
 
@@ -45,6 +53,7 @@ const Components = {
45
53
  CpHeading,
46
54
  CpButton,
47
55
  CpDate,
56
+ CpCoreDatepicker,
48
57
  CpDatepicker,
49
58
  CpCalendar,
50
59
  CpAlert,
@@ -66,6 +75,12 @@ const Pimp = {
66
75
  Object.keys(Components).forEach((name) => {
67
76
  app.component(name, Components[name])
68
77
  })
78
+
79
+ // Directives
80
+ app.directive('click-outside', ClickOutside)
81
+
82
+ // Plugins
83
+ app.use(maska)
69
84
  },
70
85
  }
71
86