@pocketprep/ui-kit 3.2.4 → 3.3.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.
@@ -1,51 +1,76 @@
1
1
  <template>
2
- <div class="uikit-calendar">
3
- <Input
2
+ <div
3
+ class="uikit-calendar"
4
+ >
5
+ <PocketInput
6
+ ref="calendarInput"
4
7
  v-model="dateString"
5
8
  :label="label"
6
9
  class="uikit-calendar__input"
7
10
  :placeholder="placeholder"
8
11
  :is-dark-mode="isDarkMode"
9
- disabled
12
+ :force-hover-style="hover"
13
+ :force-focus-style="isShowingCalendar"
14
+ :hideInputCaret="true"
15
+ :disabled="disabled"
10
16
  @update:modelValue="processDateString"
11
- @click="calendar && calendar.show()"
12
- />
13
- <div
14
- ref="calendarToggle"
15
- v-dark="isDarkMode"
16
- class="uikit-calendar__icon"
17
- :class="{ 'uikit-calendar__icon--hover': hover, 'uikit-calendar__icon--focus': focus }"
18
- tabindex="0"
19
- @keydown.stop.prevent.enter="calendar && calendar.show()"
20
- @click="calendar && calendar.show()"
17
+ @click="showCalendar"
18
+ @keydown="handleInputKeydown"
19
+ @mouseover="hover = true"
20
+ @mouseout="hover = false"
21
21
  >
22
- <Icon type="calendar" />
23
- </div>
24
- <div
25
- ref="calendar"
26
- class="uikit-calendar__trigger"
27
- :tabindex="-1"
28
- />
22
+ <div
23
+ v-if="isShowingCalendar"
24
+ v-dark="isDarkMode"
25
+ class="uikit-calendar__clear-date-icon"
26
+ @click.stop.prevent="clearCalendar"
27
+ >
28
+ <Tooltip
29
+ class="uikit-calendar__clear-date-tooltip"
30
+ v-if="showClearDateTooltip">Clear Date</Tooltip>
31
+ <Icon
32
+ type="close"
33
+ @mouseenter="showClearDateTooltip = true"
34
+ @mouseleave="showClearDateTooltip = false"
35
+ />
36
+ </div>
37
+ <div
38
+ v-dark="isDarkMode"
39
+ class="uikit-calendar__calendar-icon"
40
+ :class="{
41
+ 'uikit-calendar__calendar-icon--active': !disabled && (hover || isShowingCalendar),
42
+ }"
43
+ >
44
+ <Icon type="calendarPicker" />
45
+ </div>
46
+ <div
47
+ ref="calendar"
48
+ class="uikit-calendar__trigger"
49
+ :tabindex="-1"
50
+ />
51
+ </PocketInput>
29
52
  </div>
30
53
  </template>
31
54
 
32
55
  <script lang="ts">
33
- import type { ComponentPublicInstance } from 'vue'
34
56
  import { Vue, Component, Prop, Emit } from 'vue-facing-decorator'
35
- import Input from '../Forms/Input.vue'
57
+ import PocketInput from '../Forms/Input.vue'
36
58
  import Icon from '../Icons/Icon.vue'
59
+ import Tooltip from '../Tooltips/Tooltip.vue'
37
60
  import Litepicker from 'litepicker'
38
61
  import 'litepicker/dist/plugins/keyboardnav'
39
62
  import type { DateTime } from 'litepicker/dist/types/datetime'
40
63
  import { dark } from '../../directives'
64
+ import type { ComponentPublicInstance } from 'vue'
41
65
 
42
66
  @Component({
43
67
  directives: {
44
68
  dark,
45
69
  },
46
70
  components: {
47
- Input,
71
+ PocketInput,
48
72
  Icon,
73
+ Tooltip,
49
74
  },
50
75
  })
51
76
  export default class Calendar extends Vue {
@@ -53,13 +78,15 @@ export default class Calendar extends Vue {
53
78
  @Prop({ default: '' }) placeholder!: string
54
79
  @Prop() modelValue!: Date | null
55
80
  @Prop({ default: false }) isDarkMode!: boolean
81
+ @Prop({ default: false }) disabled!: boolean
56
82
 
57
83
  calendar: null | Litepicker = null
58
84
  calendarInput: Date | null = null
59
85
  dateString = ''
60
86
  hover = false
61
- focus = false
87
+ isShowingCalendar = false
62
88
  afterCalendarTab = -1
89
+ showClearDateTooltip = false
63
90
 
64
91
  beforeUnmount () {
65
92
  this.calendar?.destroy()
@@ -78,6 +105,12 @@ export default class Calendar extends Vue {
78
105
  this.dateString = this.calendarInput.toLocaleDateString()
79
106
  this.emitUpdateModelValue(this.calendarInput)
80
107
  })
108
+ picker.on('clear:selection', () => {
109
+ this.calendarInput = null
110
+ this.dateString = ''
111
+ this.emitUpdateModelValue(this.calendarInput)
112
+ picker.hide()
113
+ })
81
114
  picker.on('show', () => {
82
115
  this.$nextTick(() => {
83
116
  const calendarEls = Array.from<HTMLElement>(document.querySelectorAll('.litepicker'))
@@ -86,19 +119,52 @@ export default class Calendar extends Vue {
86
119
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
87
120
  ) as HTMLElement
88
121
  focusableEl.focus()
122
+ this.isShowingCalendar = true
89
123
  })
90
124
  })
91
125
  picker.on('hide', () => {
92
- const calendarToggle = ((
93
- this.$refs['calendarToggle'] as ComponentPublicInstance | undefined
94
- )?.$el)
95
- calendarToggle?.focus()
96
- calendarToggle?.blur()
126
+ const calendarInputContainer = this.$refs['calendarInput'] as ComponentPublicInstance | undefined
127
+ const calendarInputContainerEl = calendarInputContainer?.$el as HTMLDivElement | undefined
128
+ const calendarInputEl = calendarInputContainerEl?.querySelector('input')
129
+ calendarInputEl?.focus()
130
+ calendarInputEl?.blur()
131
+ this.isShowingCalendar = false
97
132
  })
98
133
  },
99
134
  })
100
135
  }
101
136
 
137
+ clearCalendar () {
138
+ if (this.disabled) {
139
+ return
140
+ }
141
+
142
+ this.calendar?.clearSelection()
143
+ }
144
+
145
+ showCalendar () {
146
+ if (this.disabled) {
147
+ return
148
+ }
149
+
150
+ this.calendar && this.calendar.show()
151
+ }
152
+
153
+ handleInputKeydown (event: KeyboardEvent) {
154
+ if (event.key === 'Backspace') {
155
+ this.clearCalendar()
156
+ return
157
+ }
158
+ if (![ 'Shift', 'Tab' ].includes(event.key)) {
159
+ if (!this.isShowingCalendar) {
160
+ this.showCalendar()
161
+ }
162
+ event.stopPropagation()
163
+ event.preventDefault()
164
+ return false
165
+ }
166
+ }
167
+
102
168
  processDateString () {
103
169
  const inputDate = new Date(this.dateString)
104
170
  if (inputDate && inputDate.toLocaleDateString() !== this.calendarInput?.toLocaleDateString()) {
@@ -108,7 +174,7 @@ export default class Calendar extends Vue {
108
174
  }
109
175
 
110
176
  @Emit('update:modelValue')
111
- emitUpdateModelValue (calendarInput: Date) {
177
+ emitUpdateModelValue (calendarInput: Date | null) {
112
178
  return calendarInput
113
179
  }
114
180
  }
@@ -126,32 +192,61 @@ export default class Calendar extends Vue {
126
192
  height: 1px;
127
193
  }
128
194
 
129
- &__icon {
195
+ &__clear-date-icon {
130
196
  position: absolute;
131
- right: 8px;
197
+ top: 30px;
198
+ right: 34px;
132
199
  bottom: 8px;
133
200
  width: 20px;
134
201
  height: 20px;
135
202
  cursor: pointer;
136
- color: $brand-blue;
203
+ color: $steel;
137
204
  outline: none;
138
205
 
139
- &:focus::before {
140
- content: '';
141
- left: -1px;
142
- top: -1px;
206
+ &--dark {
207
+ color: $pewter;
208
+ }
209
+
210
+ &:hover {
211
+ color: $brand-blue;
212
+
213
+ &--dark {
214
+ color: $banana-bread;
215
+ }
216
+ }
217
+
218
+ svg {
143
219
  width: 100%;
144
220
  height: 100%;
145
- position: absolute;
146
- border: 1px solid $brand-blue;
147
- border-radius: 5px;
148
221
  }
222
+ }
223
+
224
+ &__clear-date-tooltip {
225
+ position: relative;
226
+ bottom: 32px;
227
+ left: 10px;
228
+ }
229
+
230
+ &__calendar-icon {
231
+ position: absolute;
232
+ top: 30px;
233
+ right: 8px;
234
+ bottom: 8px;
235
+ width: 20px;
236
+ height: 20px;
237
+ cursor: pointer;
238
+ color: $steel;
239
+ outline: none;
149
240
 
150
241
  &--dark {
151
- color: $banana-bread;
242
+ color: $pewter;
243
+ }
244
+
245
+ &--active {
246
+ color: $brand-blue;
152
247
 
153
- &:focus::before {
154
- border-color: $banana-bread;
248
+ &--dark {
249
+ color: $banana-bread;
155
250
  }
156
251
  }
157
252
 
@@ -10,9 +10,10 @@
10
10
  v-dark="isDarkMode"
11
11
  class="uikit-input__label"
12
12
  :class="{
13
- 'uikit-input__label--hover': hover,
14
- 'uikit-input__label--focus': focus,
15
- 'uikit-input__label--error': error
13
+ 'uikit-input__label--hover': hover || forceHoverStyle,
14
+ 'uikit-input__label--focus': focus || forceFocusStyle,
15
+ 'uikit-input__label--error': error,
16
+ 'uikit-input__input--hide-caret': hideInputCaret,
16
17
  }"
17
18
  >{{ label }}</label>
18
19
  <div
@@ -32,9 +33,10 @@
32
33
  :tabindex="tabindex"
33
34
  class="uikit-input__input"
34
35
  :class="{
35
- 'uikit-input__input--hover': hover,
36
- 'uikit-input__input--focus': focus,
37
- 'uikit-input__input--error': error
36
+ 'uikit-input__input--hover': hover || forceHoverStyle,
37
+ 'uikit-input__input--focus': focus || forceFocusStyle,
38
+ 'uikit-input__input--error': error,
39
+ 'uikit-input__input--hide-caret': hideInputCaret,
38
40
  }"
39
41
  @input="valueChange"
40
42
  @focus="focus = true"
@@ -73,15 +75,17 @@
73
75
  :name="name"
74
76
  :aria-required="ariaRequired"
75
77
  :class="{
76
- 'uikit-input__input--hover': hover,
77
- 'uikit-input__input--focus': focus,
78
- 'uikit-input__input--error': error
78
+ 'uikit-input__input--hover': hover || forceHoverStyle,
79
+ 'uikit-input__input--focus': focus || forceFocusStyle,
80
+ 'uikit-input__input--error': error,
81
+ 'uikit-input__input--hide-caret': hideInputCaret,
79
82
  }"
80
83
  @input="valueChange"
81
84
  @focus="focus = true"
82
85
  @blur="focus = false"
83
86
  @mouseover="hover = true"
84
87
  >
88
+ <slot />
85
89
  </div>
86
90
  </template>
87
91
 
@@ -112,6 +116,9 @@ export default class Input extends Vue {
112
116
  @Prop({ default: false }) isDarkMode!: boolean
113
117
  @Prop({ default: false }) ariaRequired!: boolean
114
118
  @Prop({ default: '' }) name!: string
119
+ @Prop({ default: false }) forceHoverStyle!: boolean
120
+ @Prop({ default: false }) forceFocusStyle!: boolean
121
+ @Prop({ default: false }) hideInputCaret!: boolean
115
122
 
116
123
  hover = false
117
124
  focus = false
@@ -148,47 +155,41 @@ export default class Input extends Vue {
148
155
  background-color: $white;
149
156
  border: 1px solid rgba($pewter, 0.85);
150
157
  color: $brand-black;
151
- -webkit-text-fill-color: $brand-black;
152
- -webkit-box-shadow: 0 0 0 1000px $white inset;
153
- box-shadow: 0 0 0 1000px $white inset;
154
158
  border-radius: 3px;
155
159
  caret-color: $brand-blue;
156
160
  padding: 9px 11px;
157
- font-size: 16px;
158
- line-height: 15px;
161
+ font-size: 15px;
162
+ line-height: 20px;
159
163
  height: 36px;
160
164
  width: 100%;
161
165
  box-sizing: border-box;
162
166
  outline: none;
163
167
  z-index: 2;
164
168
 
165
- &::placeholder {
169
+ :not(&:disabled)::placeholder {
166
170
  color: $pewter;
167
- -webkit-text-fill-color: $pewter;
168
171
  }
169
172
 
170
173
  &:disabled {
171
174
  background: $gray-background;
172
- border: 1px solid $gray-background;
173
- color: $slate-03;
175
+ border: 1px solid rgba($pewter, 0.5);
176
+ color: $slate;
177
+ }
174
178
 
175
- &:placeholder {
176
- color: $slate-03;
177
- }
179
+ &:disabled::placeholder {
180
+ color: $slate;
178
181
  }
179
182
 
180
183
  &--dark {
181
184
  background-color: $moonlit-ocean;
182
- -webkit-text-fill-color: $white;
183
- -webkit-box-shadow: 0 0 0 1000px $moonlit-ocean inset;
184
- box-shadow: 0 0 0 1000px $moonlit-ocean inset;
185
185
  border-color: $pewter;
186
186
  color: $white;
187
187
  caret-color: $banana-bread;
188
188
 
189
189
  &:disabled {
190
- border-color: $moonlit-ocean;
191
- background-color: $moonlit-ocean;
190
+ border-color: rgba($pewter, 0.5);
191
+ background-color: rgba($moonlit-ocean, 0.5);
192
+ color: $steel;
192
193
  }
193
194
  }
194
195
 
@@ -196,7 +197,7 @@ export default class Input extends Vue {
196
197
  border: 1px solid $baby-blue;
197
198
 
198
199
  &--dark {
199
- border: 0.5px solid $banana-bread;
200
+ border: 1px solid rgba($banana-bread, 0.4);
200
201
  }
201
202
  }
202
203
 
@@ -204,38 +205,35 @@ export default class Input extends Vue {
204
205
  border: 1px solid $brand-blue;
205
206
 
206
207
  &--dark {
207
- border-color: $banana-bread;
208
+ border: 1px solid $banana-bread;
208
209
  }
209
210
  }
210
211
 
211
212
  &--error {
212
- border: 1px solid $red-pegasus;
213
+ border: 1px solid $pepper;
213
214
 
214
215
  &--dark {
215
- border-color: $rosa;
216
+ border: 1px solid $rosa;
216
217
  }
217
218
  }
219
+
220
+ &--hide-caret {
221
+ caret-color: transparent;
222
+ }
218
223
  }
219
224
 
220
225
  &__label {
221
226
  font-size: 13px;
222
- line-height: 14px;
223
- margin: 0 0 6px 12px;
224
- color: $slate-01;
227
+ font-weight: 600;
228
+ line-height: 18px;
229
+ margin: 0 0 4px 12px;
230
+ color: $pickled-bluewood;
225
231
  display: block;
226
232
 
227
233
  &--dark {
228
234
  color: $fog;
229
235
  }
230
236
 
231
- &--hover {
232
- color: $slate-03;
233
-
234
- &--dark {
235
- color: $fog;
236
- }
237
- }
238
-
239
237
  &--focus {
240
238
  color: $brand-blue;
241
239
 
@@ -245,12 +243,16 @@ export default class Input extends Vue {
245
243
  }
246
244
 
247
245
  &--error {
248
- color: $red-pegasus;
246
+ color: $pepper;
249
247
 
250
248
  &--dark {
251
249
  color: $rosa;
252
250
  }
253
251
  }
252
+
253
+ &--hide-caret {
254
+ caret-color: transparent;
255
+ }
254
256
  }
255
257
 
256
258
  &__password-container {
@@ -39,21 +39,23 @@
39
39
  }"
40
40
  @click="showDropdown = !showDropdown"
41
41
  >
42
- {{ modelValue ? modelValue.label : placeholder }}
43
-
44
- <div
45
- v-if="subtext"
46
- v-dark="isDarkMode"
47
- class="uikit-select__subtext"
48
- :class="{
49
- 'uikit-select__subtext--placeholder': !subtext,
50
- 'uikit-select__subtext--hover': hover,
51
- 'uikit-select__subtext--focus': focus,
52
- 'uikit-select__subtext--disabled': disabled,
53
- }"
54
- >
55
- {{ modelValue ? modelValue.subtext : subtext }}
56
- </div>
42
+ <slot name="selectValue" :item="modelValue">
43
+ {{ modelValue ? modelValue.label : placeholder }}
44
+
45
+ <div
46
+ v-if="subtext"
47
+ v-dark="isDarkMode"
48
+ class="uikit-select__subtext"
49
+ :class="{
50
+ 'uikit-select__subtext--placeholder': !subtext,
51
+ 'uikit-select__subtext--hover': hover,
52
+ 'uikit-select__subtext--focus': focus,
53
+ 'uikit-select__subtext--disabled': disabled,
54
+ }"
55
+ >
56
+ {{ modelValue ? modelValue.subtext : subtext }}
57
+ </div>
58
+ </slot>
57
59
  </div>
58
60
  <input
59
61
  v-else
@@ -113,7 +115,7 @@
113
115
  @keydown="keyPressedItem"
114
116
  @mousedown.prevent
115
117
  >
116
- <slot name="dropdownListItem">
118
+ <slot name="dropdownListItem" :item="item">
117
119
  {{ item.label }}
118
120
  <div
119
121
  v-if="item.subtext"
@@ -424,9 +426,10 @@ export default class Select extends Vue {
424
426
 
425
427
  &__label {
426
428
  font-size: 13px;
427
- line-height: 14px;
428
- margin: 0 0 6px 12px;
429
- color: $slate-01;
429
+ line-height: 18px;
430
+ font-weight: 600;
431
+ margin: 0 0 4px 12px;
432
+ color: $pickled-bluewood;
430
433
  display: block;
431
434
 
432
435
  &--dark {
@@ -568,9 +571,9 @@ export default class Select extends Vue {
568
571
  position: absolute;
569
572
  right: 13px;
570
573
  top: 14px;
571
- color: $pewter;
574
+ color: $steel;
572
575
  width: 12px;
573
- height: 7px;
576
+ height: 8px;
574
577
  z-index: 1;
575
578
 
576
579
  &--hover {
@@ -120,7 +120,7 @@ export default class Textarea extends Vue {
120
120
  border: 1px solid $gray-background;
121
121
  color: $slate-03;
122
122
 
123
- &:placeholder {
123
+ &::placeholder {
124
124
  color: $slate-03;
125
125
  }
126
126
  }
@@ -30,6 +30,7 @@
30
30
  <IconExam v-else-if="type === 'exam'" :title="title" />
31
31
  <IconLightning v-else-if="type === 'lightning'" :title="title" />
32
32
  <IconCalendar v-else-if="type === 'calendar'" :title="title" />
33
+ <IconCalendarPicker v-else-if="type === 'calendarPicker'" :title="title" />
33
34
  <IconPassage v-else-if="type === 'passage'" :title="title" />
34
35
  <IconPencil v-else-if="type === 'pencil'" :title="title" />
35
36
  <IconPeople v-else-if="type === 'people'" :title="title" />
@@ -101,6 +102,7 @@ import IconBarChart from './IconBarChart.vue'
101
102
  import IconExam from './IconExam.vue'
102
103
  import IconLightning from './IconLightning.vue'
103
104
  import IconCalendar from './IconCalendar.vue'
105
+ import IconCalendarPicker from './IconCalendarPicker.vue'
104
106
  import IconPassage from './IconPassage.vue'
105
107
  import IconPencil from './IconPencil.vue'
106
108
  import IconPeople from './IconPeople.vue'
@@ -169,6 +171,7 @@ import IconPresent from './IconPresent.vue'
169
171
  IconExam,
170
172
  IconLightning,
171
173
  IconCalendar,
174
+ IconCalendarPicker,
172
175
  IconPassage,
173
176
  IconPencil,
174
177
  IconPeople,
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <!-- eslint-disable -->
3
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <title>{{ title }}</title>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 1.75C5.5 1.19772 5.94772 0.75 6.5 0.75C7.05228 0.75 7.5 1.19772 7.5 1.75V3.25H12.5V1.75C12.5 1.19772 12.9477 0.75 13.5 0.75C14.0523 0.75 14.5 1.19772 14.5 1.75V3.25H17C18.1046 3.25 19 4.14543 19 5.25V16.25C19 17.3546 18.1046 18.25 17 18.25H3C1.89543 18.25 1 17.3546 1 16.25V5.25C1 4.14543 1.89543 3.25 3 3.25H5.5V1.75ZM3 16.25V7.75H17V16.25H3Z" fill="currentColor"/>
6
+ </svg>
7
+ <!-- eslint-enable -->
8
+ </template>
9
+
10
+ <script lang="ts">
11
+ import { Vue, Component, Prop } from 'vue-facing-decorator'
12
+
13
+ @Component
14
+ export default class IconCalendarPicker extends Vue {
15
+ @Prop() title!: string
16
+ }
17
+ </script>
@@ -25,6 +25,7 @@ export type TIconType =
25
25
  | 'exam'
26
26
  | 'lightning'
27
27
  | 'calendar'
28
+ | 'calendarPicker'
28
29
  | 'passage'
29
30
  | 'pencil'
30
31
  | 'people'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pocketprep/ui-kit",
3
- "version": "3.2.4",
3
+ "version": "3.3.1",
4
4
  "description": "Pocket Prep UI Kit",
5
5
  "author": "pocketprep",
6
6
  "scripts": {
@@ -91,7 +91,7 @@
91
91
  "npm-run-all": "4.1.5",
92
92
  "postcss-html": "1.5.0",
93
93
  "sass": "1.58.3",
94
- "stylelint": "15.1.0",
94
+ "stylelint": "15.10.3",
95
95
  "stylelint-config-recommended-scss": "9.0.1",
96
96
  "stylelint-config-recommended-vue": "1.4.0",
97
97
  "typescript": "5.0.4",