@usssa/component-library 1.0.0-alpha.131 → 1.0.0-alpha.133

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Component Library v1.0.0-alpha.130
1
+ # Component Library v1.0.0-alpha.133
2
2
 
3
3
  **This library provides custom UI components for USSSA applications.**
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usssa/component-library",
3
- "version": "1.0.0-alpha.131",
3
+ "version": "1.0.0-alpha.133",
4
4
  "description": "A Quasar component library project",
5
5
  "productName": "Quasar component library App",
6
6
  "author": "Troy Moreland <troy.moreland@usssa.com>",
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
- import { computed, ref } from 'vue'
2
+ import { computed, onMounted, onUnmounted, ref, useSlots, watch } from 'vue'
3
+ import { useScreenType } from '../../composables/useScreenType'
3
4
  import UBtnStd from './UBtnStd.vue'
4
5
 
5
6
  const emit = defineEmits(['onBackIconClick', 'hideDialog'])
@@ -14,6 +15,10 @@ const props = defineProps({
14
15
  type: String,
15
16
  default: 'dialog',
16
17
  },
18
+ dialogHeight: {
19
+ default: 850,
20
+ type: Number,
21
+ },
17
22
  divider: Boolean,
18
23
  heading: {
19
24
  type: String,
@@ -30,7 +35,19 @@ const props = defineProps({
30
35
  },
31
36
  })
32
37
 
38
+ // constants
39
+ const MAX_HEIGHT = props.dialogHeight
40
+ const MIN_HEIGHT = 120
41
+ let isDragging = false
42
+ let startHeight = 0
43
+ let startY = 0
44
+
45
+ const $screen = useScreenType()
46
+ const $slots = useSlots()
47
+
48
+ const currentDialogHeight = ref(props.dialogHeight)
33
49
  const dialogRef = ref(true)
50
+ const scrollableMarginHeight = ref(null)
34
51
 
35
52
  const dialogStyle = computed(() => ({
36
53
  '--customSize': props.customSize,
@@ -40,26 +57,140 @@ const headerClass = computed(() => {
40
57
  return props.divider ? 'divider' : ''
41
58
  })
42
59
 
60
+ const isMobile = computed(() => $screen.value.isMobile)
61
+ const isTablet = computed(() => $screen.value.isTablet)
62
+ const isDesktop = computed(() => $screen.value.isDesktop)
63
+
64
+ const getDialogHeaderheight = () => {
65
+ setTimeout(() => {
66
+ const headingWrapper =
67
+ document.getElementsByClassName('no-heading-section')[0]
68
+ if (headingWrapper) {
69
+ scrollableMarginHeight.value = headingWrapper.clientHeight
70
+ }
71
+ }, 100)
72
+ }
73
+
43
74
  const handleBackClick = () => {
44
75
  return emit('onBackIconClick')
45
76
  }
77
+
78
+ const hasAllActionSlots = () => {
79
+ return (
80
+ !!$slots['action_secondary_one'] &&
81
+ !!$slots['action_secondary_two'] &&
82
+ !!$slots['action_primary_one'] &&
83
+ !!$slots['action_primary_two']
84
+ )
85
+ }
86
+
87
+ const hasSlots = () => {
88
+ return (!!$slots['action_secondary_one'] ||
89
+ !!$slots['action_secondary_two']) &&
90
+ (!!$slots['action_primary_one'] || !!$slots['action_primary_two'])
91
+ ? '0.5rem'
92
+ : null
93
+ }
94
+
95
+ const onDragEnd = () => {
96
+ isDragging = false
97
+ window.removeEventListener('mousemove', onDragMove)
98
+ window.removeEventListener('mouseup', onDragEnd)
99
+ window.removeEventListener('touchmove', onDragMove)
100
+ window.removeEventListener('touchend', onDragEnd)
101
+ }
102
+
103
+ const onDragMove = (event) => {
104
+ if (!isDragging) return
105
+ // event.preventDefault()
106
+ const currentY = event.touches ? event.touches[0].clientY : event.clientY
107
+ const deltaY = startY - currentY
108
+ const newHeight = startHeight + deltaY
109
+ // limiting dialog height to MAX HEIGHT
110
+ currentDialogHeight.value = Math.min(
111
+ Math.max(MIN_HEIGHT, newHeight),
112
+ MAX_HEIGHT
113
+ )
114
+ if (Math.min(Math.max(MIN_HEIGHT, newHeight), MAX_HEIGHT) <= MIN_HEIGHT) {
115
+ setTimeout(() => {
116
+ dialogRef.value.hide() // if user is dragging then it should close from here
117
+ }, 200)
118
+ }
119
+ // currentDialogHeight.value = Math.max(MIN_HEIGHT, startHeight + deltaY) // if limiting not required
120
+ }
121
+
122
+ const onMouseDown = (event) => {
123
+ isDragging = true
124
+ event.preventDefault()
125
+ startY = event.clientY
126
+ startHeight = currentDialogHeight.value
127
+ window.addEventListener('mousemove', (e) => onDragMove(e))
128
+ window.addEventListener('mouseup', onDragEnd)
129
+ }
130
+
131
+ const onTouchStart = (event) => {
132
+ isDragging = true
133
+ event.preventDefault()
134
+ startY = event.touches[0].clientY
135
+ startHeight = currentDialogHeight.value
136
+ window.addEventListener('touchmove', (e) => onDragMove(e))
137
+ window.addEventListener('touchend', onDragEnd)
138
+ }
139
+
140
+ onUnmounted(() => {
141
+ window.removeEventListener('mousemove', onDragMove)
142
+ window.removeEventListener('mouseup', onDragEnd)
143
+ window.removeEventListener('touchmove', onDragMove)
144
+ window.removeEventListener('touchend', onDragEnd)
145
+ })
146
+
147
+ onMounted(() => {
148
+ getDialogHeaderheight()
149
+ })
150
+
151
+ watch(
152
+ model,
153
+ (newData) => {
154
+ getDialogHeaderheight()
155
+ if (!newData) {
156
+ currentDialogHeight.value = MAX_HEIGHT
157
+ }
158
+ },
159
+ { immediate: true }
160
+ )
46
161
  </script>
47
162
 
48
163
  <template>
49
164
  <q-dialog
50
165
  v-bind="$attrs"
51
166
  v-model="model"
52
- :class="`dialog-${size}`"
167
+ :class="`dialog-${isMobile ? 'full' : size}`"
53
168
  :dataTestId="dataTestId"
54
169
  full-width
55
170
  no-backdrop-dismiss
56
- :position="position"
171
+ :position="isMobile ? 'bottom' : position"
57
172
  ref="dialogRef"
58
173
  :style="dialogStyle"
59
174
  >
60
- <q-card class="dialog-wrapper full-height">
175
+ <q-card
176
+ :class="`dialog-wrapper${!isMobile ? ' full-height' : ''}`"
177
+ :style="{
178
+ height: isMobile ? currentDialogHeight + 'px !important' : 'auto',
179
+ overflow: isMobile ? 'hidden' : 'auto',
180
+ }"
181
+ >
182
+ <div
183
+ v-if="isMobile"
184
+ class="drag-handle-wrapper"
185
+ @mousedown="onMouseDown($event)"
186
+ @touchstart="onTouchStart($event)"
187
+ >
188
+ <div class="drag-handle" />
189
+ </div>
61
190
  <q-card-section
62
- :class="`col items-center q-pa-none heading-section ${headerClass}`"
191
+ :class="`col items-center q-pa-none heading-section ${headerClass}${
192
+ isMobile ? ' no-heading-section' : ''
193
+ }`"
63
194
  >
64
195
  <div class="heading-wrapper row">
65
196
  <div class="flex items-center dialog-heading-container">
@@ -108,25 +239,62 @@ const handleBackClick = () => {
108
239
  </UBtnStd>
109
240
  </div>
110
241
  <div class="row">
111
- <slot name="secondary_row"></slot>
242
+ <slot name="secondary_row" />
112
243
  </div>
113
244
  </q-card-section>
114
245
 
115
246
  <q-card-section
116
- class="full-height main-content-dialog scroll q-px-ba"
247
+ :class="`full-height main-content-dialog scroll q-px-ba${
248
+ isMobile ? ' scrollable-content' : ''
249
+ }`"
250
+ :style="{
251
+ 'margin-top': `${scrollableMarginHeight + (isMobile ? 16 : 0)}px`,
252
+ height:
253
+ isTablet || isDesktop
254
+ ? 'auto'
255
+ : currentDialogHeight -
256
+ (scrollableMarginHeight +
257
+ (hasSlots() && hasAllActionSlots() ? 144 : 88)) +
258
+ 'px !important',
259
+ }"
117
260
  tabindex="-1"
118
261
  >
119
262
  <slot name="content" />
120
263
  </q-card-section>
121
264
 
122
- <div v-if="showActionButtons" class="q-pa-ba action-wrapper">
123
- <q-card-actions class="action-buttons" align="left">
124
- <slot name="action_secondary_one"></slot>
125
- <slot name="action_secondary_two"></slot>
265
+ <div
266
+ v-if="showActionButtons"
267
+ :class="`q-pa-ba action-wrapper${
268
+ isMobile ? ' fixed-action-wrapper' : ''
269
+ }`"
270
+ :style="{
271
+ gap: hasSlots(),
272
+ flexWrap: isMobile && hasAllActionSlots() ? 'wrap' : 'no-wrap',
273
+ justifyContent:
274
+ (isMobile && hasAllActionSlots()) || hasSlots()
275
+ ? 'space-between'
276
+ : 'flex-end',
277
+ }"
278
+ >
279
+ <q-card-actions
280
+ v-if="
281
+ !!$slots['action_secondary_one'] || !!$slots['action_secondary_two']
282
+ "
283
+ :class="`action-buttons${isMobile ? ' full-width-actions' : ''}`"
284
+ align="left"
285
+ >
286
+ <slot name="action_secondary_one" />
287
+ <slot name="action_secondary_two" />
126
288
  </q-card-actions>
127
- <q-card-actions class="action-buttons" align="right">
128
- <slot name="action_primary_one"></slot>
129
- <slot name="action_primary_two"></slot>
289
+ <q-card-actions
290
+ v-if="
291
+ !!$slots['action_primary_one'] || !!$slots['action_primary_two']
292
+ "
293
+ :class="`action-buttons${isMobile ? ' full-width-actions' : ''}`"
294
+ align="right"
295
+ >
296
+ <slot name="action_primary_one" />
297
+ <slot name="action_primary_two" />
130
298
  </q-card-actions>
131
299
  </div>
132
300
  </q-card>
@@ -246,4 +414,55 @@ const handleBackClick = () => {
246
414
 
247
415
  #leftIconLabelRef
248
416
  display: none
417
+
418
+ .drag-handle-wrapper
419
+ width: 100%
420
+ height: 1rem
421
+ z-index: 9
422
+ display: flex
423
+ align-items: center
424
+ justify-content: center
425
+ background: #FFFFFF
426
+ position: fixed
427
+
428
+ .drag-handle
429
+ width: 2rem
430
+ height: 0.25rem
431
+ cursor: ns-resize
432
+ background: $neutral-5
433
+ border-radius: $xxl !important
434
+
435
+ .full-width-actions
436
+ width: 100%
437
+
438
+ .dialog-full .no-heading-section
439
+ padding-top: 0px !important
440
+ position: fixed
441
+ width: 100%
442
+ z-index: 99
443
+ background: white
444
+ margin-top: 0.89rem
445
+
446
+ .sheet-card-wrapper
447
+ position: relative
448
+ width: 100%
449
+ overflow: hidden
450
+ transition: height 0.02s ease-in-out
451
+ background: $neutral-1
452
+ border-radius: $sm $sm 0 0 !important
453
+ box-shadow: none !important
454
+ touch-action: none
455
+
456
+ .fixed-action-wrapper
457
+ position: fixed
458
+ bottom: 0
459
+ width: 100%
460
+ padding: $ba $sm !important
461
+
462
+ .dialog-full .scrollable-content
463
+ margin-bottom: 4.5rem !important
464
+ height: auto !important
465
+
466
+ .scrollable::-webkit-scrollbar
467
+ z-index: 9999
249
468
  </style>
@@ -1,11 +1,10 @@
1
1
  <script setup>
2
2
  import { computed } from 'vue'
3
+ import { useScreenType } from '../../composables/useScreenType'
3
4
  import URadioStd from './URadioStd.vue'
4
5
 
5
6
  const emit = defineEmits(['onClick', 'onButtonClick'])
6
-
7
7
  const model = defineModel()
8
-
9
8
  const props = defineProps({
10
9
  altText: {
11
10
  type: String,
@@ -46,6 +45,8 @@ const props = defineProps({
46
45
  },
47
46
  })
48
47
 
48
+ const $screen = useScreenType()
49
+
49
50
  const activeClass = computed(() => {
50
51
  if (model.value === props.value) {
51
52
  return 'active'
@@ -53,6 +54,8 @@ const activeClass = computed(() => {
53
54
  return ''
54
55
  })
55
56
 
57
+ const isMobile = computed(() => $screen.value.isMobile)
58
+
56
59
  const handleChange = () => {
57
60
  model.value = props.value
58
61
  }
@@ -60,33 +63,44 @@ const handleChange = () => {
60
63
 
61
64
  <template>
62
65
  <q-btn
63
- :class="`u-radio-btn ${activeClass}`"
66
+ :class="`u-radio-btn ${activeClass}${isMobile ? ' full-width' : ''}`"
64
67
  :dataTestId="dataTestId"
65
68
  flat
66
69
  @click="handleChange"
67
70
  >
68
- <div class="radio-btn-content flex items-center">
69
- <URadioStd v-model="model" :aria-label="label" :id="id" :value="value" />
70
- <div class="button-text">
71
- <div class="text-caption-lg">{{ label }}</div>
72
- <div class="description-text text-body-sm">{{ description }}</div>
71
+ <div class="radio-btn-content flex items-center justify-between full-width">
72
+ <div
73
+ class="flex items-center justify-center radio-btn-text-wrapper no-wrap"
74
+ >
75
+ <URadioStd
76
+ v-model="model"
77
+ :aria-label="label"
78
+ :id="id"
79
+ :value="value"
80
+ />
81
+ <div class="button-text">
82
+ <div class="text-caption-lg">{{ label }}</div>
83
+ <div class="description-text text-body-sm">{{ description }}</div>
84
+ </div>
73
85
  </div>
74
86
 
75
- <img
76
- v-if="image"
77
- class="radio-btn-img"
78
- :alt="altText"
79
- :aria-label="imgAriaLabel"
80
- size="1.5rem"
81
- :src="image"
82
- />
83
- <q-icon
84
- v-if="iconClass"
85
- :class="iconClass"
86
- :aria-label="imgAriaLabel"
87
- :color="iconColor"
88
- size="1.5rem"
89
- />
87
+ <div class="flex items-center">
88
+ <img
89
+ v-if="image"
90
+ class="radio-btn-img"
91
+ :alt="altText"
92
+ :aria-label="imgAriaLabel"
93
+ size="1.5rem"
94
+ :src="image"
95
+ />
96
+ <q-icon
97
+ v-if="iconClass"
98
+ :class="iconClass"
99
+ :aria-label="imgAriaLabel"
100
+ :color="iconColor"
101
+ size="1.5rem"
102
+ />
103
+ </div>
90
104
  </div>
91
105
  </q-btn>
92
106
  </template>
@@ -123,4 +137,7 @@ const handleChange = () => {
123
137
 
124
138
  .q-radio .q-radio__inner
125
139
  color: $primary !important
140
+
141
+ .radio-btn-text-wrapper
142
+ gap: $xs
126
143
  </style>
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { computed } from 'vue'
2
+ import { computed, ref, watch } from 'vue'
3
3
  import UAvatar from './UAvatar.vue'
4
4
  import UMenuItem from './UMenuItem.vue'
5
5
  import UTooltip from './UTooltip.vue'
@@ -84,6 +84,8 @@ const props = defineProps({
84
84
  },
85
85
  })
86
86
 
87
+ const placeholderText = ref(props?.placeholder)
88
+
87
89
  const model = computed({
88
90
  get() {
89
91
  return props.modelValue
@@ -117,6 +119,17 @@ const handleKeydown = () => {
117
119
  }
118
120
  }, 100)
119
121
  }
122
+
123
+ const updateVal = (val) => {
124
+ placeholderText.value = val?.length ? '' : props?.placeholder
125
+ }
126
+
127
+ watch(
128
+ () => props?.placeholder,
129
+ (value) => {
130
+ placeholderText.value = value
131
+ }
132
+ )
120
133
  </script>
121
134
 
122
135
  <template>
@@ -160,10 +173,11 @@ const handleKeydown = () => {
160
173
  :option-value="optionValue"
161
174
  :options="options"
162
175
  outlined
163
- :placeholder="placeholder"
176
+ :placeholder="placeholderText"
164
177
  :use-input="useInput"
165
178
  @filter="filterFn"
166
179
  @keydown="handleKeydown"
180
+ @update:model-value="(val) => updateVal(val)"
167
181
  >
168
182
  <template v-slot:no-option>
169
183
  <q-item>
@@ -11,7 +11,7 @@ const props = defineProps({
11
11
  },
12
12
  closeIconLabel: { type: String, default: 'close Icon' },
13
13
  dialogClass: {
14
- type: String
14
+ type: String,
15
15
  },
16
16
  closeIconLabel: {
17
17
  type: String,
@@ -178,88 +178,82 @@ watch(
178
178
  :transition-hide="getDialogTransitions(dialog.position).hide"
179
179
  :transition-show="getDialogTransitions(dialog.position).show"
180
180
  >
181
- <q-card
182
- class="sheet-card-wrapper"
183
- :style="{
184
- height: dialog.height + 'px',
185
- }"
181
+ <q-card
182
+ class="sheet-card-wrapper"
183
+ :style="{
184
+ height: dialog.height + 'px',
185
+ }"
186
+ >
187
+ <div
188
+ class="drag-handle-wrapper"
189
+ @mousedown="onMouseDown(key, $event)"
190
+ @touchstart="onTouchStart(key, $event)"
186
191
  >
187
- <div
188
- class="drag-handle-wrapper"
189
- @mousedown="onMouseDown(key, $event)"
190
- @touchstart="onTouchStart(key, $event)"
191
- >
192
- <div class="drag-handle" />
193
- </div>
192
+ <div class="drag-handle" />
193
+ </div>
194
194
 
195
- <q-card-section
196
- class="row items-start justify-between no-wrap q-pt-none q-pb-xs q-px-ba"
197
- >
198
- <div class="heading-wrapper row">
199
- <div
200
- class="flex items-center justify-center dialog-heading-container"
195
+ <q-card-section
196
+ class="row items-start justify-between no-wrap q-pt-none q-pb-xs q-px-ba q-mt-ba"
197
+ >
198
+ <div class="heading-wrapper row">
199
+ <div
200
+ class="flex items-center justify-center dialog-heading-container"
201
+ >
202
+ <UBtnStd
203
+ v-if="isLeftIcon"
204
+ class="dialog-action-icons q-mr-xs"
205
+ :aria-label="leftIconLabel"
206
+ :flat="true"
207
+ tabindex="0"
208
+ @click="handleBackClick()"
201
209
  >
202
- <UBtnStd
203
- v-if="isLeftIcon"
204
- class="dialog-action-icons q-mr-xs"
205
- :aria-label="leftIconLabel"
206
- :flat="true"
207
- tabindex="0"
208
- @click="handleBackClick()"
209
- >
210
- <q-icon
211
- :class="`icon-close ${leftIcon}`"
212
- size="1.5rem"
213
- tabindex="-1"
214
- />
215
- </UBtnStd>
210
+ <q-icon
211
+ :class="`icon-close ${leftIcon}`"
212
+ size="1.5rem"
213
+ tabindex="-1"
214
+ />
215
+ </UBtnStd>
216
+ <div>
217
+ <div class="text-heading-xs">
218
+ {{ heading }}
219
+ </div>
216
220
  <div>
217
- <div class="text-heading-xs">
218
- {{ heading }}
219
- </div>
220
- <div>
221
- <span
222
- v-if="headingCaption"
223
- class="text-body-sm dialog-caption"
224
- >
225
- {{ headingCaption }}
226
- </span>
227
- </div>
221
+ <span v-if="headingCaption" class="text-body-sm dialog-caption">
222
+ {{ headingCaption }}
223
+ </span>
228
224
  </div>
229
225
  </div>
230
226
  </div>
227
+ </div>
231
228
 
232
- <UBtnStd
233
- class="dialog-action-icons"
234
- :aria-label="closeIconLabel"
235
- :flat="true"
236
- tabindex="0"
237
- @click="handleCloseDialog(key)"
238
- >
239
- <q-icon
240
- :class="`icon-close ${closeIcon} icon-secondary-opacity`"
241
- size="1.5rem"
242
- tabindex="-1"
243
- />
244
- </UBtnStd>
245
- </q-card-section>
246
- <q-card-section
247
- class="main-content-dialog scroll q-px-ba"
248
- :style="{ height: dialog.height - 84 + 'px !important' }"
249
- tabindex="-1"
229
+ <UBtnStd
230
+ class="dialog-action-icons"
231
+ :aria-label="closeIconLabel"
232
+ :flat="true"
233
+ tabindex="0"
234
+ @click="handleCloseDialog(key)"
250
235
  >
251
- <slot name="content"></slot>
252
- </q-card-section>
253
- </q-card>
254
- <div
255
- v-if="showActionButtons && dialog.height > 70"
256
- class="action-wrapper"
236
+ <q-icon
237
+ :class="`icon-close ${closeIcon} icon-secondary-opacity`"
238
+ size="1.5rem"
239
+ tabindex="-1"
240
+ />
241
+ </UBtnStd>
242
+ </q-card-section>
243
+ <q-card-section
244
+ class="main-content-dialog scroll q-px-ba"
245
+ :style="{ height: dialog.height - 84 + 'px !important' }"
246
+ tabindex="-1"
257
247
  >
258
- <q-card-actions class="action-buttons q-px-sm q-py-ba" align="center">
259
- <slot name="action_primary_one"></slot>
260
- <slot name="action_primary_two"></slot>
261
- </q-card-actions>
262
- </div>
248
+ <slot name="content"></slot>
249
+ </q-card-section>
250
+ </q-card>
251
+ <div v-if="showActionButtons && dialog.height > 70" class="action-wrapper">
252
+ <q-card-actions class="action-buttons q-px-sm q-py-ba" align="center">
253
+ <slot name="action_primary_one"></slot>
254
+ <slot name="action_primary_two"></slot>
255
+ </q-card-actions>
256
+ </div>
263
257
  </q-dialog>
264
258
  </template>
265
259