@usssa/component-library 1.0.0-alpha.122 → 1.0.0-alpha.124

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.121
1
+ # Component Library v1.0.0-alpha.124
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.122",
3
+ "version": "1.0.0-alpha.124",
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,11 +1,12 @@
1
1
  <script setup>
2
2
  import { computed } from 'vue'
3
3
  import UTooltip from './UTooltip.vue'
4
+
5
+ const emit = defineEmits(['onClick'])
4
6
  const props = defineProps({
5
- iconClass: {
7
+ anchor: {
6
8
  type: String,
7
- required: true,
8
- default: 'fa-kit-duotone fa-circle-bolt',
9
+ default: 'center left',
9
10
  },
10
11
  ariaLabel: {
11
12
  type: String,
@@ -15,10 +16,27 @@ const props = defineProps({
15
16
  type: String,
16
17
  default: 'primary',
17
18
  },
19
+ dataTestId: {
20
+ type: String,
21
+ default: 'button-icon',
22
+ },
23
+ iconClass: {
24
+ type: String,
25
+ required: true,
26
+ default: 'fa-kit-duotone fa-circle-bolt',
27
+ },
28
+ offset: {
29
+ type: Array,
30
+ default: () => [4, 4],
31
+ },
18
32
  round: {
19
33
  type: Boolean,
20
34
  default: true,
21
35
  },
36
+ self: {
37
+ type: String,
38
+ default: 'center end',
39
+ },
22
40
  size: {
23
41
  type: String,
24
42
  default: 'md',
@@ -32,26 +50,20 @@ const props = defineProps({
32
50
  type: String,
33
51
  default: 'top',
34
52
  },
35
- anchor: {
36
- type: String,
37
- default: 'center left',
38
- },
39
- self: {
40
- type: String,
41
- default: 'center end',
42
- },
43
- offset: {
44
- type: Array,
45
- default: () => [4, 4],
46
- },
47
53
  })
48
54
 
49
- const emit = defineEmits(['onClick'])
50
-
51
- const handleClick = () => {
52
- return emit('onClick')
53
- }
54
-
55
+ const btnClass = computed(() => {
56
+ if (props.color === 'neutral') {
57
+ return 'neutral-4'
58
+ }
59
+ return props.color
60
+ })
61
+ const iconColor = computed(() => {
62
+ if (props.color === 'neutral') {
63
+ return 'neutral-9'
64
+ }
65
+ return props.color
66
+ })
55
67
  const iconSize = computed(() => {
56
68
  if (props.size === 'sm') {
57
69
  return 'xs'
@@ -62,47 +74,37 @@ const iconSize = computed(() => {
62
74
  if (props.size === 'lg') {
63
75
  return 'md'
64
76
  }
65
-
66
77
  return props.size
67
78
  })
68
- const iconColor = computed(() => {
69
- if (props.color === 'neutral') {
70
- return 'neutral-9'
71
- }
72
- return props.color
73
- })
74
79
 
75
- const btnClass = computed(() => {
76
- if (props.color === 'neutral') {
77
- return 'neutral-4'
78
- }
79
- return props.color
80
- })
80
+ const handleClick = () => {
81
+ return emit('onClick')
82
+ }
81
83
  </script>
82
84
 
83
85
  <template>
84
86
  <q-btn
85
- @click="handleClick"
87
+ :class="`u-btn-icon size-${size} ${btnClass}`"
88
+ :aria-label="ariaLabel"
89
+ :dataTestId="dataTestId"
86
90
  flat="flat"
87
91
  :round="round"
88
- :aria-label="ariaLabel"
89
- class="u-btn-icon"
90
- :class="`size-${size} ${btnClass}`"
92
+ @click="handleClick"
91
93
  >
92
94
  <template #default>
93
95
  <q-icon
94
- :color="iconColor"
95
- :aria-hidden="true"
96
96
  :class="`${iconClass} size-${size}`"
97
+ :aria-hidden="true"
98
+ :color="iconColor"
97
99
  :size="iconSize"
98
100
  />
99
101
 
100
102
  <UTooltip
101
103
  v-if="tooltip"
102
- :description="tooltip"
103
104
  :anchor="anchor"
104
- :self="self"
105
+ :description="tooltip"
105
106
  :offset="offset"
107
+ :self="self"
106
108
  />
107
109
  <slot name="menu" />
108
110
  </template>
@@ -10,7 +10,7 @@ const props = defineProps({
10
10
  },
11
11
  dataTestId: {
12
12
  type: String,
13
- default: 'button-std'
13
+ default: 'button-std',
14
14
  },
15
15
  disable: {
16
16
  type: Boolean,
@@ -31,6 +31,11 @@ const props = defineProps({
31
31
  leftIcon: {
32
32
  type: String,
33
33
  },
34
+ leftIconSize: {
35
+ type: String,
36
+ default: 'ba',
37
+ validator: (val) => ['sm', 'md', 'lg', 'ms', 'ba'].includes(val),
38
+ },
34
39
  outline: {
35
40
  type: Boolean,
36
41
  default: false,
@@ -45,9 +50,12 @@ const props = defineProps({
45
50
  },
46
51
  })
47
52
 
48
- const handleClick = () => {
49
- return emit('onClick')
50
- }
53
+ const isFullWidth = computed(() => {
54
+ if (props.fullWidth === true) {
55
+ return 'full-width'
56
+ }
57
+ return ''
58
+ })
51
59
 
52
60
  const textClass = computed(() => {
53
61
  if (props.color === 'neutral') {
@@ -56,25 +64,21 @@ const textClass = computed(() => {
56
64
  return ''
57
65
  })
58
66
 
59
- const isFullWidth = computed(() => {
60
- if (props.fullWidth === true) {
61
- return 'full-width'
62
- }
63
- return ''
64
- })
67
+ const handleClick = () => {
68
+ return emit('onClick')
69
+ }
65
70
  </script>
66
71
 
67
72
  <template>
68
73
  <q-btn
69
- class="u-btn q-py-none"
70
- :class="`size-${size} ${textClass} focus-${color} ${isFullWidth}`"
74
+ :class="`u-btn q-py-none size-${size} ${textClass} focus-${color} ${isFullWidth}`"
71
75
  :color="color"
72
76
  :dataTestId="dataTestId"
73
77
  :disable="disable"
74
78
  :flat="flat"
75
79
  no-caps
76
- :unelevated="!outline"
77
80
  :outline="outline"
81
+ :unelevated="!outline"
78
82
  @click="handleClick"
79
83
  >
80
84
  <template #default>
@@ -82,10 +86,9 @@ const isFullWidth = computed(() => {
82
86
  <div class="row items-center no-wrap">
83
87
  <q-icon
84
88
  v-if="leftIcon"
85
- :class="leftIcon"
86
- class="q-mr-xs"
89
+ :class="`q-mr-xs ${leftIcon}`"
87
90
  left
88
- size="1rem"
91
+ :size="leftIconSize"
89
92
  />
90
93
 
91
94
  <div class="text-center text-caption-md button-label">
@@ -93,8 +96,7 @@ const isFullWidth = computed(() => {
93
96
  </div>
94
97
  <q-icon
95
98
  v-if="rightIcon"
96
- :class="rightIcon"
97
- class="q-ml-xs"
99
+ :class="`q-ml-xs ${rightIcon}`"
98
100
  right
99
101
  size="1rem"
100
102
  />
@@ -123,12 +125,10 @@ const isFullWidth = computed(() => {
123
125
  min-height: $lg
124
126
  padding: 0 $ba !important
125
127
 
126
-
127
128
  &.size-lg
128
129
  min-height: $xl
129
130
  padding: 0 $ba !important
130
131
 
131
-
132
132
  .button-label
133
133
  word-break: break-all
134
134
  </style>
@@ -17,21 +17,25 @@ const open = defineModel('open', {
17
17
  type: Boolean,
18
18
  })
19
19
  const props = defineProps({
20
- menu: {
21
- default: () => [],
22
- type: Array,
23
- },
24
20
  brandLogo: {
25
21
  default: '',
26
22
  },
27
23
  brandMiniLogo: {
28
24
  default: '',
29
25
  },
26
+ dataTestId:{
27
+ default:'drawer-std',
28
+ type: String
29
+ },
30
30
  homeLink: {
31
31
  required: true,
32
32
  default: '/home',
33
33
  type: String,
34
34
  },
35
+ menu: {
36
+ default: () => [],
37
+ type: Array,
38
+ }
35
39
  })
36
40
 
37
41
  const $router = useRouter()
@@ -64,6 +68,7 @@ const handleCloseDrawer = () => {
64
68
  v-model="open"
65
69
  :class="`u-drawer ${miniState ? 'u-mini-drawer' : ''}`"
66
70
  :breakpoint="400"
71
+ :dataTestId="dataTestId"
67
72
  :mini="miniState"
68
73
  :mini-width="64"
69
74
  show-if-above
@@ -183,7 +188,7 @@ const handleCloseDrawer = () => {
183
188
  outline: auto !important
184
189
  .q-focus-helper
185
190
  display: none
186
-
191
+
187
192
  .u-drawer-scrollable-area
188
193
  height: calc(100% - 0px)
189
194
  margin-top: 0px
@@ -7,6 +7,10 @@ const emit = defineEmits(['update:modelValue', 'onRowChange', 'onPageChange'])
7
7
  const model = defineModel()
8
8
 
9
9
  const props = defineProps({
10
+ dataTestId:{
11
+ type: String,
12
+ default:'pagination-std'
13
+ },
10
14
  maxPageLink: {
11
15
  //Maximum number of page links to display at a time
12
16
  type: Number,
@@ -62,6 +66,7 @@ watch(currentPage, (newValue) => {
62
66
  active-color="primary"
63
67
  boundary-numbers
64
68
  color="dark"
69
+ :dataTestId="dataTestId"
65
70
  direction-links
66
71
  flat
67
72
  gutter="sm"
@@ -11,6 +11,10 @@ const props = defineProps({
11
11
  type: String,
12
12
  default: 'neutral-7',
13
13
  },
14
+ dataTestId:{
15
+ type: String,
16
+ default: 'select-std'
17
+ },
14
18
  disableAvatar: {
15
19
  type: Boolean,
16
20
  default: true,
@@ -44,19 +48,16 @@ const props = defineProps({
44
48
  type: String,
45
49
  required: false,
46
50
  },
47
- options: {
48
- type: Array,
49
- required: true,
50
- },
51
- optionValue: {
52
- type: String,
53
- },
54
51
  optionLabel: {
55
52
  type: String,
56
53
  },
57
- toolTipText: {
54
+ optionValue: {
58
55
  type: String,
59
56
  },
57
+ options: {
58
+ type: Array,
59
+ required: true,
60
+ },
60
61
  placeholder: {
61
62
  type: String,
62
63
  default: 'Select',
@@ -74,6 +75,9 @@ const props = defineProps({
74
75
  default: 'md',
75
76
  validator: (val) => ['sm', 'md', 'lg'].includes(val),
76
77
  },
78
+ toolTipText: {
79
+ type: String,
80
+ },
77
81
  useInput: {
78
82
  type: Boolean,
79
83
  required: false,
@@ -116,7 +120,7 @@ const handleKeydown = () => {
116
120
  </script>
117
121
 
118
122
  <template>
119
- <section class="column q-gutter-y-xs">
123
+ <section class="column q-gutter-y-xs" :dataTestId="dataTestId">
120
124
  <label class="row items-center" for="select">
121
125
  <div class="u-select-label text-body-sm">
122
126
  <span>{{ label }}</span>
@@ -143,6 +147,7 @@ const handleKeydown = () => {
143
147
  v-model="model"
144
148
  :class="`u-select field-${size}`"
145
149
  :popup-content-class="`u-options-menu ${popupClass}`"
150
+ options-selected-class="primary bg-blue-1"
146
151
  bottom-slots
147
152
  :color="color"
148
153
  emit-value
@@ -154,7 +159,6 @@ const handleKeydown = () => {
154
159
  :option-label="optionLabel"
155
160
  :option-value="optionValue"
156
161
  :options="options"
157
- options-selected-class="primary bg-blue-1"
158
162
  outlined
159
163
  :placeholder="placeholder"
160
164
  :use-input="useInput"
@@ -0,0 +1,317 @@
1
+ <script setup>
2
+ import { onUnmounted, watch } from 'vue'
3
+ import UBtnStd from './UBtnStd.vue'
4
+
5
+ const emit = defineEmits(['onBackIconClick'])
6
+ const dialogs = defineModel('dialogs', { default: () => [], type: Array })
7
+ const props = defineProps({
8
+ closeIcon: {
9
+ default: 'fa-kit-duotone fa-circle-xmark',
10
+ type: String,
11
+ },
12
+ closeIconLabel: { type: String, default: 'close Icon' },
13
+ heading: {
14
+ default: 'Select Option',
15
+ type: String,
16
+ },
17
+ headingCaption: {
18
+ default: null,
19
+ type: String,
20
+ },
21
+ isLeftIcon: { type: Boolean, default: false },
22
+ leftIcon: { type: String, default: 'fa-kit-duotone fa-circle-arrow-left' },
23
+ leftIconLabel: { type: String, default: 'Back Icon' },
24
+ showActionButtons: {
25
+ default: false,
26
+ type: Boolean,
27
+ },
28
+ })
29
+
30
+ // constants
31
+ const MAX_HEIGHT = 750
32
+ const MIN_HEIGHT = 40
33
+ let isDragging = false
34
+ let startHeight = 0
35
+ let startY = 0
36
+
37
+ // custom functions
38
+ const getDialogTransitions = (position) => {
39
+ let defaultTransition = { show: 'slide-up', hide: 'slide-down' }
40
+ if (position === 'right') {
41
+ defaultTransition = { show: 'slide-left', hide: 'slide-right' }
42
+ } else if (position === 'left') {
43
+ defaultTransition = { show: 'slide-right', hide: 'slide-left' }
44
+ }
45
+ return defaultTransition
46
+ }
47
+
48
+ const handleBackClick = () => {
49
+ return emit('onBackIconClick')
50
+ }
51
+
52
+ const handleCloseDialog = (index) => {
53
+ setTimeout(() => {
54
+ if (dialogs.value[index]) {
55
+ dialogs.value[index].open = false
56
+ }
57
+ }, 100)
58
+ // removing closed dialog data from the dialogs
59
+ setTimeout(() => {
60
+ dialogs.value.splice(index, 1)
61
+ }, 500)
62
+ }
63
+
64
+ const onDragEnd = () => {
65
+ isDragging = false
66
+ window.removeEventListener('mousemove', onDragMove)
67
+ window.removeEventListener('mouseup', onDragEnd)
68
+ window.removeEventListener('touchmove', onDragMove)
69
+ window.removeEventListener('touchend', onDragEnd)
70
+ }
71
+
72
+ const onDragMove = (index, event) => {
73
+ if (!isDragging || index !== dialogs.value.length - 1) return
74
+ event.preventDefault()
75
+ const currentY = event.touches ? event.touches[0].clientY : event.clientY
76
+ const deltaY = startY - currentY
77
+ const newHeight = startHeight + deltaY
78
+ // limiting dialog height to MAX HEIGHT
79
+ dialogs.value[index].height = Math.min(
80
+ Math.max(MIN_HEIGHT, newHeight),
81
+ MAX_HEIGHT
82
+ )
83
+ if (Math.min(Math.max(MIN_HEIGHT, newHeight), MAX_HEIGHT) <= MIN_HEIGHT) {
84
+ setTimeout(() => {
85
+ dialogs.value.splice(index, 1) // if user is dragging then it should close from here
86
+ }, 200)
87
+ }
88
+ // dialogs.value[index].height = Math.max(MIN_HEIGHT, startHeight + deltaY) // if limiting not required
89
+ }
90
+
91
+ const onMouseDown = (index, event) => {
92
+ if (index !== dialogs.value.length - 1) return // Only allow dragging for the topmost dialog
93
+ event.preventDefault()
94
+ isDragging = true
95
+ startY = event.clientY
96
+ startHeight = dialogs.value[index].height
97
+ window.addEventListener('mousemove', (e) => onDragMove(index, e))
98
+ window.addEventListener('mouseup', onDragEnd)
99
+ }
100
+
101
+ const onTouchStart = (index, event) => {
102
+ if (index !== dialogs.value.length - 1) return // Only allow dragging for the topmost dialog
103
+ event.preventDefault()
104
+ isDragging = true
105
+ startY = event.touches[0].clientY
106
+ startHeight = dialogs.value[index].height
107
+ window.addEventListener('touchmove', (e) => onDragMove(index, e))
108
+ window.addEventListener('touchend', onDragEnd)
109
+ }
110
+
111
+ onUnmounted(() => {
112
+ window.removeEventListener('mousemove', onDragMove)
113
+ window.removeEventListener('mouseup', onDragEnd)
114
+ window.removeEventListener('touchmove', onDragMove)
115
+ window.removeEventListener('touchend', onDragEnd)
116
+ })
117
+
118
+ watch(
119
+ dialogs,
120
+ (newVal) => {
121
+ const len = newVal.length
122
+ if (len > 1) {
123
+ if (dialogs.value[len - 2]) {
124
+ dialogs.value[len - 1].height = dialogs.value[len - 2].height // opening same height of previous dialog
125
+ }
126
+ }
127
+ },
128
+ { immediate: true }
129
+ )
130
+
131
+ watch(
132
+ dialogs, // to watch deep and for nested dialog
133
+ () => {
134
+ const index = dialogs.value.findIndex((dialog) => !dialog.open)
135
+ if (index > -1) {
136
+ handleCloseDialog(index)
137
+ }
138
+ },
139
+ { immediate: true, deep: true }
140
+ )
141
+ </script>
142
+
143
+ <template>
144
+ <q-dialog
145
+ v-for="(dialog, key) in dialogs"
146
+ v-model="dialog.open"
147
+ :key="key"
148
+ :persistent="
149
+ typeof dialog.persistent === 'undefined' ? true : dialog.persistent
150
+ "
151
+ position="bottom"
152
+ :transition-duration="
153
+ dialog.transitionDuration ? dialog.transitionDuration : 300
154
+ "
155
+ :transition-hide="getDialogTransitions(dialog.position).hide"
156
+ :transition-show="getDialogTransitions(dialog.position).show"
157
+ >
158
+ <div>
159
+ <q-card
160
+ class="sheet-card-wrapper"
161
+ :style="{
162
+ height: dialog.height + 'px',
163
+ }"
164
+ >
165
+ <div
166
+ class="drag-handle-wrapper"
167
+ @mousedown="onMouseDown(key, $event)"
168
+ @touchstart="onTouchStart(key, $event)"
169
+ >
170
+ <div class="drag-handle" />
171
+ </div>
172
+
173
+ <q-card-section
174
+ class="row items-start justify-between no-wrap q-pt-none q-pb-xs q-px-ba"
175
+ >
176
+ <div class="heading-wrapper row">
177
+ <div
178
+ class="flex items-center justify-center dialog-heading-container"
179
+ >
180
+ <UBtnStd
181
+ v-if="isLeftIcon"
182
+ class="dialog-action-icons q-mr-xs"
183
+ :aria-label="leftIconLabel"
184
+ :flat="true"
185
+ tabindex="0"
186
+ @click="handleBackClick()"
187
+ >
188
+ <q-icon
189
+ :class="`icon-close ${leftIcon}`"
190
+ size="1.5rem"
191
+ tabindex="-1"
192
+ />
193
+ </UBtnStd>
194
+ <div>
195
+ <div class="text-heading-xs">
196
+ {{ heading }}
197
+ </div>
198
+ <div>
199
+ <span
200
+ v-if="headingCaption"
201
+ class="text-body-sm dialog-caption"
202
+ >
203
+ {{ headingCaption }}
204
+ </span>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <UBtnStd
211
+ class="dialog-action-icons"
212
+ :aria-label="closeIconLabel"
213
+ :flat="true"
214
+ tabindex="0"
215
+ @click="handleCloseDialog(key)"
216
+ >
217
+ <q-icon
218
+ :class="`icon-close ${closeIcon} icon-secondary-opacity`"
219
+ size="1.5rem"
220
+ tabindex="-1"
221
+ />
222
+ </UBtnStd>
223
+ </q-card-section>
224
+ <q-card-section
225
+ class="main-content-dialog scroll q-px-ba"
226
+ :style="{ height: dialog.height - 84 + 'px !important' }"
227
+ tabindex="-1"
228
+ >
229
+ <slot name="content"></slot>
230
+ </q-card-section>
231
+ </q-card>
232
+ <div
233
+ v-if="showActionButtons && dialog.height > 70"
234
+ class="action-wrapper"
235
+ >
236
+ <q-card-actions class="action-buttons q-px-sm" align="center">
237
+ <slot name="action_primary_one"></slot>
238
+ <slot name="action_primary_two"></slot>
239
+ </q-card-actions>
240
+ </div>
241
+ </div>
242
+ </q-dialog>
243
+ </template>
244
+
245
+ <style lang="sass">
246
+ .sheet-card-wrapper
247
+ position: relative
248
+ width: 100%
249
+ overflow: hidden
250
+ transition: height 0.02s ease-in-out
251
+ background: $neutral-1
252
+ border-radius: $sm $sm 0 0 !important
253
+ box-shadow: none !important
254
+ touch-action: none
255
+
256
+ .q-btn.dialog-action-icons
257
+ padding: 0px 0px !important
258
+ min-width: 1.5rem !important
259
+ min-height: 0px !important
260
+ height: fit-content !important
261
+ border-radius: 0px
262
+
263
+ &:focus
264
+ outline: auto !important
265
+
266
+ .q-focus-helper
267
+ background: transparent !important
268
+
269
+ .drag-handle-wrapper
270
+ width: 100%
271
+ height: 1rem
272
+ z-index: 9
273
+ display: flex
274
+ align-items: center
275
+ justify-content: center
276
+
277
+ .drag-handle
278
+ width: 2rem
279
+ height: 0.25rem
280
+ cursor: ns-resize
281
+ background: $neutral-5
282
+ border-radius: $xxl !important
283
+
284
+ .outer-wrapper
285
+ overflow: auto
286
+
287
+ .action-buttons
288
+ display: flex
289
+ align-items: center
290
+ gap: $xs
291
+ flex-wrap: nowrap
292
+ .q-btn
293
+ margin: 0 !important
294
+ width: 50%
295
+
296
+ .destructive-item
297
+ .q-item__section
298
+ color: red !important
299
+
300
+ .action-wrapper
301
+ background: $neutral-1
302
+
303
+ .main-content-dialog
304
+ margin-bottom: $ba
305
+ padding-bottom: 0
306
+
307
+ .dialog-caption
308
+ color: $neutral-9
309
+
310
+ .heading-wrapper
311
+ justify-content: space-between
312
+ flex-wrap: nowrap
313
+ margin-bottom: 0px !important
314
+
315
+ .dialog-heading-container
316
+ gap: $xs
317
+ </style>
@@ -0,0 +1,60 @@
1
+ <script setup>
2
+ const emit = defineEmits(['toggleDrawer'])
3
+ const props = defineProps({
4
+ menuOpen: {
5
+ type: Boolean,
6
+ default: true,
7
+ },
8
+ })
9
+ const toggleDrawer = () => {
10
+ emit('toggleDrawer')
11
+ }
12
+
13
+ console.log('menu', props.menuOpen)
14
+ </script>
15
+
16
+ <template>
17
+ <div class="menu-container q-ml-ba" @click="toggleDrawer">
18
+ <div class="bar top" :class="{ open: !menuOpen }" />
19
+ <div class="bar bottom" :class="{ open: !menuOpen }" />
20
+ </div>
21
+ </template>
22
+
23
+ <style lang="sass">
24
+ $bar-width: 1.25rem
25
+ $bar-height: 0.125rem
26
+ $tap-area: $md
27
+ $transition-speed: 0.2s
28
+
29
+ .menu-container
30
+ width: $tap-area
31
+ height: $tap-area
32
+ display: flex
33
+ justify-content: center
34
+ align-items: center
35
+ position: relative
36
+ cursor: pointer
37
+
38
+ .bar
39
+ width: $bar-width
40
+ height: $bar-height
41
+ background-color: black
42
+ position: absolute
43
+ transition: transform $transition-speed ease-in-out
44
+
45
+ &.top
46
+ top: 50%
47
+ transform: translateY(-4px)
48
+
49
+ &.bottom
50
+ top: 50%
51
+ transform: translateY(4px)
52
+
53
+ /* Morphing into X */
54
+ &.open
55
+ &.top
56
+ transform: translateY(0) rotate(-45deg)
57
+
58
+ &.bottom
59
+ transform: translateY(0) rotate(45deg)
60
+ </style>
@@ -0,0 +1,206 @@
1
+ <script setup>
2
+ import { computed } from 'vue'
3
+ import UBtnIcon from '../UBtnIcon.vue'
4
+ import UBtnStd from '../UBtnStd.vue'
5
+ import UCustomMenuIcon from './UCustomMenuIcon.vue'
6
+ import { useScreenType } from '../../../composables/useScreenType'
7
+
8
+ const emit = defineEmits(['navigateBack', 'toggleDrawer', 'onLogoClick'])
9
+ const props = defineProps({
10
+ dataTestId: {
11
+ type: String,
12
+ default: 'toolbar',
13
+ },
14
+ isMiniState: {
15
+ type: Boolean,
16
+ required: true,
17
+ },
18
+ isSubPage: {
19
+ type: Boolean,
20
+ required: true,
21
+ },
22
+ title: {
23
+ type: String,
24
+ required: true,
25
+ },
26
+ })
27
+ const $screen = useScreenType()
28
+
29
+ // Computed property for the title text
30
+ const pageTitle = computed(() => {
31
+ return props.isSubPage ? `Back to ${props.title} ` : props.title
32
+ })
33
+
34
+ // Function to handle the back navigation
35
+ const handleClick = () => {
36
+ emit('navigateBack')
37
+ }
38
+
39
+ const onLogoClick = () => {
40
+ emit('onLogoClick')
41
+ }
42
+
43
+ const toggleDrawer = () => {
44
+ emit('toggleDrawer')
45
+ }
46
+ </script>
47
+
48
+ <template>
49
+ <q-toolbar
50
+ v-if="$screen.isTablet"
51
+ class="u-header-toolbar-container"
52
+ :dataTestId="dataTestId"
53
+ >
54
+ <UCustomMenuIcon
55
+ aria-label="Menu icon"
56
+ :menuOpen="isMiniState"
57
+ tabindex="0"
58
+ @toggleDrawer="toggleDrawer"
59
+ />
60
+
61
+ <section class="fit row justify-between items-center q-ml-xxs q-mr-ba">
62
+ <UBtnStd
63
+ class="q-pa-none"
64
+ aria-label="usssa logo"
65
+ flat
66
+ :ripple="false"
67
+ @click="onLogoClick"
68
+ >
69
+ <img
70
+ class="mini-state-logo full-logo"
71
+ alt="usssa-logo"
72
+ aria-label="usssa logo"
73
+ src="/images/logo.svg"
74
+ />
75
+ </UBtnStd>
76
+
77
+ <!-- Action element container -->
78
+ <div class="u-header-toolbar-action-container">
79
+ <slot name="right_section" />
80
+ </div>
81
+ </section>
82
+ </q-toolbar>
83
+
84
+ <q-toolbar
85
+ v-if="$screen.isMobile"
86
+ class="u-header-toolbar-container"
87
+ :dataTestId="dataTestId"
88
+ >
89
+ <section class="fit row items-center q-mr-ba">
90
+ <UCustomMenuIcon
91
+ aria-label="Menu icon"
92
+ :menuOpen="isMiniState"
93
+ tabindex="0"
94
+ @toggleDrawer="toggleDrawer"
95
+ />
96
+
97
+ <div class="logo-container-mb items-center justify-center">
98
+ <!-- Add logo click emitter -->
99
+ <UBtnStd
100
+ class="q-pa-none"
101
+ aria-label="usssa logo"
102
+ flat
103
+ :ripple="false"
104
+ size="sm"
105
+ @click="onLogoClick"
106
+ >
107
+ <img
108
+ class="mini-state-logo full-logo"
109
+ alt="usssa-logo"
110
+ aria-label="usssa logo"
111
+ src="/images/logo.svg"
112
+ />
113
+ </UBtnStd>
114
+ </div>
115
+
116
+ <!-- Action element container -->
117
+ <div class="u-header-toolbar-action-container">
118
+ <slot name="right_section" />
119
+ </div>
120
+ </section>
121
+ </q-toolbar>
122
+
123
+ <q-toolbar
124
+ v-if="$screen.isDesktop"
125
+ class="u-header-toolbar-container"
126
+ :dataTestId="dataTestId"
127
+ >
128
+ <!-- Expand icon to open sidebar or drawer-->
129
+ <UBtnIcon
130
+ v-if="isMiniState"
131
+ class="q-ml-xxs"
132
+ ariaLabel="Sidebar expand icon"
133
+ color="primary"
134
+ dataTestId="expand-button"
135
+ iconClass="fa-kit-duotone fa-sidebar-expand"
136
+ size="md"
137
+ />
138
+
139
+ <section class="fit row justify-between items-center q-ml-xs q-mr-ba">
140
+ <!-- Title container with dynamic title and optional back button -->
141
+ <UBtnStd
142
+ v-if="isSubPage"
143
+ class="back_to_button icon-secondary-opacity"
144
+ :aria-label="pageTitle"
145
+ color="primary"
146
+ dataTestId="back-button"
147
+ :flat="true"
148
+ :label="pageTitle"
149
+ leftIconSize="ms"
150
+ left-icon="fa-kit-duotone fa-circle-arrow-left"
151
+ tabindex="0"
152
+ @click="handleClick"
153
+ />
154
+ <span
155
+ v-else
156
+ class="q-my-none text-heading-xxs q-ml-xxs"
157
+ dataTestId="toolbar-title"
158
+ tabindex="0"
159
+ >
160
+ {{ pageTitle }}
161
+ </span>
162
+
163
+ <!-- Action element container -->
164
+ <div class="u-header-toolbar-action-container">
165
+ <slot name="right_section" />
166
+ </div>
167
+ </section>
168
+ </q-toolbar>
169
+ </template>
170
+
171
+ <style lang="sass">
172
+ .u-header-toolbar-container
173
+ background-color: white
174
+ width: 100%
175
+ padding: 0px
176
+ display: flex
177
+ align-items: center
178
+ align-self: stretch
179
+ padding: $xs 0
180
+
181
+ .full-logo
182
+ width: 5.918rem
183
+ height: $md
184
+ margin-top: $xxs
185
+
186
+ .logo-container-mb
187
+ flex: 1
188
+ display: flex
189
+
190
+ .u-header-toolbar-inner-container
191
+ width: 100%
192
+
193
+ .u-header-toolbar-title-container
194
+ display: flex
195
+ align-items: center
196
+ gap: $xs
197
+
198
+ .u-header-toolbar-action-container
199
+ display: flex
200
+ align-items: center
201
+ gap: $sm
202
+
203
+ .back_to_button
204
+ .button-label
205
+ color: $dark !important
206
+ </style>
@@ -1,23 +1,27 @@
1
1
  <script setup>
2
- import { computed, useSlots } from 'vue'
2
+ import { computed, useSlots } from 'vue'
3
3
 
4
4
  const props = defineProps({
5
- icon: {
5
+ anchor: {
6
6
  type: String,
7
7
  },
8
- description: {
8
+ dataTestId: {
9
9
  type: String,
10
- required: true,
10
+ default: 'tooltip-std',
11
11
  },
12
- anchor: {
12
+ description: {
13
13
  type: String,
14
+ required: true,
14
15
  },
15
- self: {
16
+ icon: {
16
17
  type: String,
17
18
  },
18
19
  offset: {
19
20
  type: Array,
20
21
  },
22
+ self: {
23
+ type: String,
24
+ },
21
25
  target: {
22
26
  type: String,
23
27
  default: '',
@@ -40,17 +44,18 @@ const getTooltipText = computed(() => {
40
44
 
41
45
  <template>
42
46
  <q-tooltip
43
- v-if="getTooltipText || slots.item"
44
47
  v-bind="$attrs"
48
+ v-if="getTooltipText || slots.item"
45
49
  class="u-tooltip shadow-2 bg-dark row"
46
50
  :anchor="anchor"
51
+ :dataTestId="dataTestId"
47
52
  :offset="offset"
48
53
  :self="self"
49
54
  :target="target"
50
55
  >
51
56
  <div class="row no-wrap tooltip-content">
52
57
  <div v-if="icon" class="flex items-center">
53
- <q-icon :class="icon" size="1rem"></q-icon>
58
+ <q-icon :class="icon" size="1rem" />
54
59
  </div>
55
60
  <div class="text-body-xs tooltip-text">{{ getTooltipText }}</div>
56
61
  <slot name="item" />
@@ -23,19 +23,22 @@ import UMenuDropdownAdvancedSearch from './core/UMenuDropdownAdvancedSearch.vue'
23
23
  import UMenuItem from './core/UMenuItem.vue'
24
24
  import UMenuSearch from './core/UMenuSearch.vue'
25
25
  import UMultiSelectStd from './core/UMultiSelectStd.vue'
26
+ import UPagination from './core/UPagination.vue'
26
27
  import URadioBtn from './core/URadioBtn.vue'
27
28
  import URadioStd from './core/URadioStd.vue'
28
29
  import USelectStd from './core/USelectStd.vue'
30
+ import USheet from './core/USheet.vue'
29
31
  import UTabBtnStd from './core/UTabBtnStd.vue'
30
32
  import UTableStd from './core/UTableStd.vue'
31
33
  import UTabsStd from './core/UTabsStd.vue'
32
34
  import UToggleStd from './core/UToggleStd.vue'
33
- import UToolbar from './core/UToolbar.vue'
35
+ import UToolbar from './core/UToolbar/UToolbar.vue'
34
36
  import UTooltip from './core/UTooltip.vue'
35
37
  import UUploader from './core/UUploader.vue'
36
38
 
37
39
  import { useNotify } from '../composables/useNotify.js'
38
40
  import { useOverlayLoader } from '../composables/useOverlayLoader.js'
41
+ import { useScreenType } from '../composables/useScreenType.js'
39
42
 
40
43
  export {
41
44
  UAvatar,
@@ -62,9 +65,11 @@ export {
62
65
  UMenuItem,
63
66
  UMenuSearch,
64
67
  UMultiSelectStd,
68
+ UPagination,
65
69
  URadioBtn,
66
70
  URadioStd,
67
71
  USelectStd,
72
+ USheet,
68
73
  UTabBtnStd,
69
74
  UTableStd,
70
75
  UTabsStd,
@@ -74,4 +79,5 @@ export {
74
79
  UUploader,
75
80
  useNotify,
76
81
  useOverlayLoader,
82
+ useScreenType,
77
83
  }
@@ -0,0 +1,30 @@
1
+ import { computed, ref, watch } from 'vue'
2
+ import { useQuasar } from 'quasar'
3
+
4
+ export const useScreenType = () => {
5
+ const $q = useQuasar()
6
+
7
+ const getScreenType = () => {
8
+ if ($q.screen.xs) return 'mobile' // Extra small screens (phones)
9
+ if ($q.screen.sm) return 'tablet' // Small screens (tablets)
10
+ return 'desktop' // Default is desktop
11
+ }
12
+
13
+ const screenType = ref(getScreenType())
14
+
15
+ const screen = computed(() => ({
16
+ isDesktop: screenType.value === 'desktop',
17
+ isMobile: screenType.value === 'mobile',
18
+ isTablet: screenType.value === 'tablet',
19
+ }))
20
+
21
+ // Watch for Quasar's screen changes and update `screenType`
22
+ watch(
23
+ () => $q.screen.name,
24
+ () => {
25
+ screenType.value = getScreenType()
26
+ }
27
+ )
28
+
29
+ return screen
30
+ }
@@ -1,101 +0,0 @@
1
- <script setup>
2
- import { computed } from 'vue'
3
- import UBtnIcon from './UBtnIcon.vue'
4
- import UBtnStd from './UBtnStd.vue'
5
-
6
- const emit = defineEmits(['navigateBack'])
7
- const props = defineProps({
8
- dataTestId: {
9
- type: String,
10
- default: 'toolbar',
11
- },
12
- isMiniState: {
13
- type: Boolean,
14
- required: true,
15
- },
16
- isSubPage: {
17
- type: Boolean,
18
- required: true,
19
- },
20
- title: {
21
- type: String,
22
- required: true,
23
- },
24
- })
25
-
26
- // Computed property for the title text
27
- const pageTitle = computed(() => {
28
- return props.isSubPage ? `Back to ${props.title} ` : props.title
29
- })
30
-
31
- // Function to handle the back navigation
32
- const handleClick = () => {
33
- emit('navigateBack')
34
- }
35
- </script>
36
-
37
- <template>
38
- <q-toolbar class="u-header-toolbar-container" :dataTestId="dataTestId">
39
- <!-- Expand icon to open sidebar or drawer-->
40
- <UBtnIcon
41
- v-if="isMiniState"
42
- class="q-ml-xxs"
43
- ariaLabel="Sidebar expand icon"
44
- color="primary"
45
- dataTestId="expand-button"
46
- iconClass="fa-kit-duotone fa-sidebar-shrink"
47
- size="md"
48
- />
49
-
50
- <section class="fit row justify-between items-center q-mx-ms">
51
- <!-- Title container with dynamic title and optional back button -->
52
- <UBtnStd
53
- v-if="isSubPage"
54
- class="back_to_button"
55
- :aria-label="pageTitle"
56
- color="primary"
57
- dataTestId="back-button"
58
- :flat="true"
59
- :label="pageTitle"
60
- left-icon="fa-kit-duotone fa-circle-arrow-left"
61
- @click="handleClick"
62
- />
63
- <span v-else class="q-my-none text-heading-sm" dataTestId="toolbar-title">
64
- {{ pageTitle }}
65
- </span>
66
-
67
- <!-- Action element container -->
68
- <div class="u-header-toolbar-action-container">
69
- <slot name="right_section" />
70
- </div>
71
- </section>
72
- </q-toolbar>
73
- </template>
74
-
75
- <style lang="sass">
76
- .u-header-toolbar-container
77
- background-color: white
78
- width: 100%
79
- padding: 0px
80
- display: flex
81
- align-items: center
82
- align-self: stretch
83
- padding: $sm 0
84
-
85
- .u-header-toolbar-inner-container
86
- width: 100%
87
-
88
- .u-header-toolbar-title-container
89
- display: flex
90
- align-items: center
91
- gap: $xs
92
-
93
- .u-header-toolbar-action-container
94
- display: flex
95
- align-items: center
96
- gap: $sm
97
-
98
- .back_to_button
99
- .button-label
100
- color: $dark !important
101
- </style>