@pocketprep/ui-kit 3.8.4 → 3.9.0

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 (159) hide show
  1. package/dist/@pocketprep/ui-kit.css +1 -1
  2. package/dist/@pocketprep/ui-kit.js +14469 -17731
  3. package/dist/@pocketprep/ui-kit.js.map +1 -1
  4. package/dist/@pocketprep/ui-kit.umd.cjs +19 -29
  5. package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
  6. package/eslint.config.ts +38 -11
  7. package/lib/SVGDefinitions.vue +32 -35
  8. package/lib/components/Banners/Banner.vue +10 -14
  9. package/lib/components/Blobs/Blob.vue +6 -14
  10. package/lib/components/Blobs/BlobEmptyState.vue +9 -8
  11. package/lib/components/Blobs/blob.d.ts +1 -1
  12. package/lib/components/BundleIcons/BundleIcon.vue +36 -63
  13. package/lib/components/BundleIcons/bundleIcon.d.ts +1 -1
  14. package/lib/components/Bundles/BundleList.vue +71 -59
  15. package/lib/components/Bundles/BundleSearch.vue +93 -117
  16. package/lib/components/Bundles/PremiumPill.vue +6 -12
  17. package/lib/components/Buttons/Button.vue +32 -35
  18. package/lib/components/Buttons/Link.vue +32 -31
  19. package/lib/components/Buttons/Tab.vue +14 -17
  20. package/lib/components/Calendar/Calendar.vue +87 -85
  21. package/lib/components/Charts/Bar.vue +192 -263
  22. package/lib/components/Charts/Pie.vue +55 -61
  23. package/lib/components/Charts/highcharts-wrap.ts +81 -0
  24. package/lib/components/Controls/SegmentControl.vue +26 -24
  25. package/lib/components/Controls/Slider.vue +51 -47
  26. package/lib/components/Controls/ToggleSwitch.vue +33 -31
  27. package/lib/components/EmptyStates/EmptyState.vue +69 -73
  28. package/lib/components/Exams/ExamCard.vue +59 -47
  29. package/lib/components/Exams/ExamMenuCard.vue +30 -28
  30. package/lib/components/Filters/FilterDropdown.vue +83 -86
  31. package/lib/components/Filters/FilterOptions.vue +83 -88
  32. package/lib/components/Forms/Checkbox.vue +27 -27
  33. package/lib/components/Forms/CheckboxOption.vue +30 -30
  34. package/lib/components/Forms/Errors.vue +21 -24
  35. package/lib/components/Forms/Input.vue +71 -59
  36. package/lib/components/Forms/Radio.vue +2 -2
  37. package/lib/components/Forms/RadioButton.vue +8 -8
  38. package/lib/components/Forms/Select.vue +265 -257
  39. package/lib/components/Forms/Textarea.vue +49 -35
  40. package/lib/components/Icons/IconAccordionArrow.vue +7 -9
  41. package/lib/components/Icons/IconActivity.vue +7 -9
  42. package/lib/components/Icons/IconAdd.vue +7 -11
  43. package/lib/components/Icons/IconAddCircle.vue +7 -9
  44. package/lib/components/Icons/IconArrow.vue +7 -9
  45. package/lib/components/Icons/IconBarChart.vue +7 -9
  46. package/lib/components/Icons/IconCalendar.vue +7 -9
  47. package/lib/components/Icons/IconCalendarPicker.vue +7 -9
  48. package/lib/components/Icons/IconChat.vue +7 -9
  49. package/lib/components/Icons/IconCheck.vue +7 -9
  50. package/lib/components/Icons/IconClose.vue +7 -9
  51. package/lib/components/Icons/IconConcept.vue +1 -1
  52. package/lib/components/Icons/IconCorrect.vue +7 -9
  53. package/lib/components/Icons/IconEdit.vue +7 -11
  54. package/lib/components/Icons/IconExam.vue +7 -9
  55. package/lib/components/Icons/IconExternalLink.vue +7 -9
  56. package/lib/components/Icons/IconEyeHide.vue +7 -9
  57. package/lib/components/Icons/IconEyeShow.vue +7 -9
  58. package/lib/components/Icons/IconFilter.vue +7 -9
  59. package/lib/components/Icons/IconFilterActive.vue +7 -9
  60. package/lib/components/Icons/IconFlag.vue +7 -9
  61. package/lib/components/Icons/IconFlagContent.vue +8 -9
  62. package/lib/components/Icons/IconFlagFeedback.vue +8 -10
  63. package/lib/components/Icons/IconFlagFilled.vue +7 -9
  64. package/lib/components/Icons/IconFullView.vue +7 -9
  65. package/lib/components/Icons/IconFullViewActive.vue +7 -9
  66. package/lib/components/Icons/IconGridDrag.vue +2 -2
  67. package/lib/components/Icons/IconHandle.vue +7 -9
  68. package/lib/components/Icons/IconHeart.vue +7 -9
  69. package/lib/components/Icons/IconHelp.vue +7 -9
  70. package/lib/components/Icons/IconHighlight.vue +2 -2
  71. package/lib/components/Icons/IconHourglass.vue +7 -9
  72. package/lib/components/Icons/IconImage.vue +7 -9
  73. package/lib/components/Icons/IconIncorrect.vue +7 -9
  74. package/lib/components/Icons/IconInfo.vue +7 -9
  75. package/lib/components/Icons/IconKeyboard.vue +7 -9
  76. package/lib/components/Icons/IconLaunch.vue +7 -9
  77. package/lib/components/Icons/IconLevelUp.vue +7 -9
  78. package/lib/components/Icons/IconLightbulb.vue +7 -9
  79. package/lib/components/Icons/IconLightning.vue +7 -9
  80. package/lib/components/Icons/IconLink.vue +7 -9
  81. package/lib/components/Icons/IconList.vue +7 -9
  82. package/lib/components/Icons/IconLoading.vue +7 -9
  83. package/lib/components/Icons/IconLoading2.vue +11 -11
  84. package/lib/components/Icons/IconLock.vue +7 -9
  85. package/lib/components/Icons/IconMissedQuestions.vue +7 -9
  86. package/lib/components/Icons/IconMoon.vue +7 -9
  87. package/lib/components/Icons/IconPaginationArrow.vue +7 -9
  88. package/lib/components/Icons/IconPaginationArrowDouble.vue +7 -9
  89. package/lib/components/Icons/IconPassage.vue +7 -9
  90. package/lib/components/Icons/IconPencil.vue +7 -9
  91. package/lib/components/Icons/IconPeople.vue +7 -9
  92. package/lib/components/Icons/IconPercent.vue +7 -9
  93. package/lib/components/Icons/IconPerson.vue +8 -9
  94. package/lib/components/Icons/IconPresent.vue +7 -9
  95. package/lib/components/Icons/IconPreview.vue +7 -9
  96. package/lib/components/Icons/IconQuestions.vue +7 -9
  97. package/lib/components/Icons/IconQuick10.vue +7 -9
  98. package/lib/components/Icons/IconQuickActions.vue +2 -2
  99. package/lib/components/Icons/IconRecommendedFilter.vue +1 -1
  100. package/lib/components/Icons/IconRemoveCircle.vue +7 -9
  101. package/lib/components/Icons/IconReviewFlag.vue +7 -9
  102. package/lib/components/Icons/IconSearch.vue +7 -9
  103. package/lib/components/Icons/IconShare.vue +7 -9
  104. package/lib/components/Icons/IconSideBar.vue +7 -9
  105. package/lib/components/Icons/IconSideBarActive.vue +7 -9
  106. package/lib/components/Icons/IconStar.vue +1 -1
  107. package/lib/components/Icons/IconStopwatch.vue +7 -9
  108. package/lib/components/Icons/IconStrike.vue +7 -9
  109. package/lib/components/Icons/IconSubject.vue +7 -9
  110. package/lib/components/Icons/IconText.vue +7 -9
  111. package/lib/components/Icons/IconTimer.vue +8 -9
  112. package/lib/components/Icons/IconWarning.vue +7 -9
  113. package/lib/components/Icons/icon.d.ts +1 -1
  114. package/lib/components/Loaders/SkeletonLoader.vue +1 -5
  115. package/lib/components/Modal/Modal.vue +23 -29
  116. package/lib/components/Modal/ModalContainer.vue +135 -133
  117. package/lib/components/Onboarding/EmailAuth.vue +66 -70
  118. package/lib/components/Onboarding/MagicCodeEntry.vue +88 -83
  119. package/lib/components/Pagination/QuestionReviewPagination.vue +3 -3
  120. package/lib/components/Pagination/TablePagination.vue +47 -44
  121. package/lib/components/PhonePerson/PhonePerson.vue +18 -18
  122. package/lib/components/PhonePerson/phonePerson.d.ts +1 -1
  123. package/lib/components/Quiz/FlagToggle.vue +45 -44
  124. package/lib/components/Quiz/GlobalMetricsToggle.vue +29 -28
  125. package/lib/components/Quiz/KeyboardShortcutsButton.vue +16 -23
  126. package/lib/components/Quiz/KeyboardShortcutsModal.vue +36 -37
  127. package/lib/components/Quiz/Question/BuildListChoicesContainer.vue +65 -65
  128. package/lib/components/Quiz/Question/ChoicesContainer.vue +5 -5
  129. package/lib/components/Quiz/Question/DropdownExplanation.vue +5 -5
  130. package/lib/components/Quiz/Question/Explanation.vue +6 -6
  131. package/lib/components/Quiz/Question/MPMCChoicesContainer.vue +17 -17
  132. package/lib/components/Quiz/Question/MPMCRadioGroup.vue +2 -2
  133. package/lib/components/Quiz/Question/MatrixChoicesContainer.vue +39 -39
  134. package/lib/components/Quiz/Question/MatrixRadioGroup.vue +6 -6
  135. package/lib/components/Quiz/Question/MobileMatrixChoicesContainer.vue +27 -28
  136. package/lib/components/Quiz/Question/MobileMatrixRadioGroup.vue +2 -2
  137. package/lib/components/Quiz/Question/PassageAndImage.vue +3 -3
  138. package/lib/components/Quiz/Question/PassageAndImageDropdown.vue +7 -7
  139. package/lib/components/Quiz/Question/Paywall.vue +2 -2
  140. package/lib/components/Quiz/Question/QuestionContext.vue +1 -1
  141. package/lib/components/Quiz/Question/StatsSummary.vue +2 -2
  142. package/lib/components/Quiz/Question/Summary.vue +11 -11
  143. package/lib/components/Quiz/Question.vue +90 -82
  144. package/lib/components/Quiz/QuizContainer.vue +1 -1
  145. package/lib/components/Quiz/QuizProgressBar.vue +23 -23
  146. package/lib/components/Quiz/question.d.ts +3 -3
  147. package/lib/components/Search/Pill.vue +16 -19
  148. package/lib/components/Search/Search.vue +52 -47
  149. package/lib/components/SidePanels/SidePanel.vue +168 -174
  150. package/lib/components/Tables/Table.vue +135 -122
  151. package/lib/components/Tables/TableActions.vue +81 -76
  152. package/lib/components/Tables/table.d.ts +1 -1
  153. package/lib/components/Tags/Tag.vue +49 -39
  154. package/lib/components/Toasts/Toast.vue +44 -42
  155. package/lib/components/Tooltips/OverflowTooltip.vue +39 -45
  156. package/lib/components/Tooltips/Tooltip.vue +69 -70
  157. package/lib/directives.ts +4 -4
  158. package/lib/utils.ts +13 -12
  159. package/package.json +27 -28
@@ -2,8 +2,8 @@
2
2
  <div
3
3
  v-dark="isDarkMode"
4
4
  class="uikit-exam-card"
5
- :class="{
6
- 'uikit-exam-card--cancel': showCancelButton,
5
+ :class="{
6
+ 'uikit-exam-card--cancel': showCancelButton,
7
7
  'uikit-exam-card--disable-interaction': disableInteraction,
8
8
  'uikit-exam-card--exam-selected': !showCancelButton && !disableInteraction && isSelected
9
9
  }"
@@ -13,7 +13,7 @@
13
13
  @mousedown.prevent
14
14
  >
15
15
  <div
16
- v-if="showCancelButton"
16
+ v-if="showCancelButton"
17
17
  class="uikit-exam-card__cancel"
18
18
  tabindex="0"
19
19
  aria-label="Clear exam selection"
@@ -41,7 +41,7 @@
41
41
  </span> Subjects
42
42
  </div>
43
43
  <span
44
- class="uikit-exam-card__label"
44
+ class="uikit-exam-card__label"
45
45
  aria-hidden="true"
46
46
  :style="{ color: bundleColor || '' }"
47
47
  >
@@ -56,56 +56,68 @@
56
56
  </div>
57
57
  </template>
58
58
 
59
- <script lang="ts">
60
- import type { ComponentPublicInstance } from 'vue'
61
- import { Vue, Component, Prop, Emit } from 'vue-facing-decorator'
59
+ <script setup lang="ts">
60
+ import { onMounted, ref, useTemplateRef, type ComponentPublicInstance } from 'vue'
62
61
  import BundleIcon from '../BundleIcons/BundleIcon.vue'
63
62
  import Icon from '../Icons/Icon.vue'
64
- import type { Study } from '@pocketprep/types'
65
- import { dark } from '../../directives'
63
+ import { dark as vDark } from '../../directives'
66
64
  import PremiumPill from '../Bundles/PremiumPill.vue'
67
65
 
68
- @Component({
69
- directives: {
70
- dark,
71
- },
72
- components: {
73
- BundleIcon,
74
- Icon,
75
- PremiumPill,
76
- },
77
- })
78
- export default class ExamCard extends Vue {
79
- @Prop() exam!: Study.Class.ExamMetadataJSON
80
- @Prop() bundle!: Study.Class.BundleJSON
81
- @Prop({ default: false }) showCancelButton!: boolean
82
- @Prop({ default: false }) disableInteraction!: boolean
83
- @Prop({ default: false }) isPremium!: boolean
84
- @Prop({ default: false }) isSelected!: boolean
85
- @Prop({ default: false }) isDarkMode!: boolean
86
-
87
- bundleColor: string | null = null
88
-
89
- mounted () {
90
- try {
91
- const bundleIcon = this.$refs.bundleIcon as ComponentPublicInstance
92
- const gId = bundleIcon.$el?.querySelector('path')?.getAttribute('fill')?.split('#')[1].split(')')[0]
93
- const stopColor = document.querySelectorAll(`#${gId} stop`)[1]?.getAttribute('stop-color')
94
- this.bundleColor = stopColor || null
95
- } catch (e) {
96
- // noop
97
- }
66
+ const {
67
+ exam,
68
+ bundle,
69
+ showCancelButton = false,
70
+ disableInteraction = false,
71
+ isPremium = false,
72
+ isSelected = false,
73
+ isDarkMode = false,
74
+ } = defineProps<{
75
+ exam: {
76
+ nativeAppName: string
77
+ descriptiveName: string
78
+ itemCount: number
79
+ archivedCount: number
80
+ knowledgeAreas: Record<string, unknown>
98
81
  }
99
-
100
- @Emit('select')
101
- emitSelect () {
102
- return true
82
+ bundle: {
83
+ objectId: string
84
+ name: string
103
85
  }
86
+ showCancelButton?: boolean
87
+ disableInteraction?: boolean
88
+ isPremium?: boolean
89
+ isSelected?: boolean
90
+ isDarkMode?: boolean
91
+ }>()
92
+
93
+ const emit = defineEmits<{
94
+ 'select': []
95
+ 'cancel': []
96
+ }>()
104
97
 
105
- @Emit('cancel')
106
- emitCancel () {
107
- return true
98
+ const bundleIcon = useTemplateRef<ComponentPublicInstance>('bundleIcon')
99
+ const bundleColor = ref<string | null>(null)
100
+
101
+ onMounted(() => {
102
+ try {
103
+ const gId = bundleIcon.value?.$el
104
+ ?.querySelector('path')
105
+ ?.getAttribute('fill')
106
+ ?.split('#')[1]
107
+ ?.split(')')[0]
108
+ const stopColor = document.querySelectorAll(`#${gId} stop`)[1]?.getAttribute('stop-color')
109
+ bundleColor.value = stopColor || null
110
+ } catch (e) {
111
+ // noop
108
112
  }
113
+ })
114
+
115
+ const emitSelect = () => {
116
+ emit('select')
117
+ }
118
+
119
+ const emitCancel = () => {
120
+ emit('cancel')
109
121
  }
110
122
  </script>
111
123
 
@@ -254,4 +266,4 @@ export default class ExamCard extends Vue {
254
266
  margin-right: 4px;
255
267
  }
256
268
  }
257
- </style>
269
+ </style>
@@ -40,38 +40,40 @@
40
40
  </div>
41
41
  </template>
42
42
 
43
- <script lang="ts">
44
- import { Vue, Component, Prop, Emit } from 'vue-facing-decorator'
43
+ <script setup lang="ts">
45
44
  import Icon from '../Icons/Icon.vue'
46
45
  import BundleIcon from '../BundleIcons/BundleIcon.vue'
47
46
  import PremiumPill from '../Bundles/PremiumPill.vue'
48
- import type { Study } from '@pocketprep/types'
49
-
50
- @Component({
51
- components: {
52
- BundleIcon,
53
- Icon,
54
- PremiumPill,
55
- },
56
- })
57
- export default class ExamMenuCard extends Vue {
58
- @Prop() exam!: Study.Class.ExamMetadataJSON
59
- @Prop() bundleId!: string
60
- @Prop({ default: 'color' }) theme!: 'color' | 'grayscale' | 'silver'
61
- @Prop({ default: false }) isPremium!: boolean
62
- @Prop({ default: false }) showMenuArrow!: boolean
63
- @Prop({ default: 'down' }) menuArrowDirection!: 'up' | 'down'
64
- @Prop({ default: true }) isClickable!: boolean
65
-
66
- @Emit('click')
67
- emitClick (event: Event) {
68
- return event
69
- }
70
47
 
71
- @Emit('keydown')
72
- emitKeydown (keyEvent: KeyboardEvent) {
73
- return keyEvent
74
- }
48
+ const {
49
+ exam,
50
+ bundleId,
51
+ theme = 'color',
52
+ isPremium = false,
53
+ showMenuArrow = false,
54
+ menuArrowDirection = 'down',
55
+ isClickable = true,
56
+ } = defineProps<{
57
+ exam: { nativeAppName: string; descriptiveName: string }
58
+ bundleId: string
59
+ theme?: 'color' | 'grayscale' | 'silver'
60
+ isPremium?: boolean
61
+ showMenuArrow?: boolean
62
+ menuArrowDirection?: 'up' | 'down'
63
+ isClickable?: boolean
64
+ }>()
65
+
66
+ const emit = defineEmits<{
67
+ 'click': [event: Event]
68
+ 'keydown': [keyEvent: KeyboardEvent]
69
+ }>()
70
+
71
+ const emitClick = (event: Event) => {
72
+ emit('click', event)
73
+ }
74
+
75
+ const emitKeydown = (keyEvent: KeyboardEvent) => {
76
+ emit('keydown', keyEvent)
75
77
  }
76
78
  </script>
77
79
 
@@ -5,7 +5,7 @@
5
5
  :has-icon="true"
6
6
  :aria-expanded="showFilterDropdown ? 'true' : 'false'"
7
7
  class="uikit-filter__link"
8
- :class="{
8
+ :class="{
9
9
  'uikit-filter__link--open': showFilterDropdown,
10
10
  'uikit-filter__link--responsive': responsiveButton,
11
11
  }"
@@ -15,13 +15,13 @@
15
15
  @keypress.enter="() => showFilterDropdown = !showFilterDropdown"
16
16
  @click="() => showFilterDropdown = !showFilterDropdown"
17
17
  >
18
- <Icon
18
+ <Icon
19
19
  type="filter"
20
20
  class="uikit-filter__icon"
21
21
  />
22
- <span
22
+ <span
23
23
  class="uikit-filter__text"
24
- :class="{
24
+ :class="{
25
25
  'uikit-filter__text--responsive': responsiveButton,
26
26
  }"
27
27
  >
@@ -33,8 +33,8 @@
33
33
  v-dark="isDarkMode"
34
34
  class="uikit-filter__dropdown"
35
35
  :class="{
36
- [`uikit-filter__dropdown--${align}`]: true,
37
- [`uikit-filter__dropdown--${size}`]: true
36
+ [`uikit-filter__dropdown--${align}`]: true,
37
+ [`uikit-filter__dropdown--${size}`]: true
38
38
  }"
39
39
  >
40
40
  <slot name="filterOptions">
@@ -53,102 +53,99 @@
53
53
  </div>
54
54
  </template>
55
55
 
56
- <script lang="ts">
57
- import { Component, Emit, Prop, Vue, Watch } from 'vue-facing-decorator'
56
+ <script setup lang="ts">
58
57
  import Link from '../Buttons/Link.vue'
59
58
  import Icon from '../Icons/Icon.vue'
60
59
  import FilterOptions from '../Filters/FilterOptions.vue'
61
- import { dark } from '../../directives'
60
+ import { dark as vDark } from '../../directives'
62
61
  import type { TRadioOption, TCheckboxOption } from './filterOptions'
63
-
64
- @Component({
65
- components: {
66
- FilterOptions,
67
- Icon,
68
- Link,
69
- },
70
- directives: {
71
- dark,
72
- },
62
+ import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
63
+
64
+ const {
65
+ radioEntries,
66
+ checkboxEntries,
67
+ filterLabel,
68
+ align = 'right',
69
+ size = 'small',
70
+ isDarkMode = false,
71
+ responsiveButton = false,
72
+ filterOptionsLabel = 'FILTER BY SUBJECT',
73
+ } = defineProps<{
74
+ radioEntries: TRadioOption[]
75
+ checkboxEntries: TCheckboxOption[]
76
+ filterLabel: string
77
+ align?: 'left' | 'right'
78
+ size?: 'small' | 'large'
79
+ isDarkMode?: boolean
80
+ responsiveButton?: boolean
81
+ filterOptionsLabel?: string
82
+ }>()
83
+
84
+ const emit = defineEmits<{
85
+ 'update:modelValue': [options: TCheckboxOption[] | TRadioOption[]]
86
+ }>()
87
+
88
+ const radioOptions = ref<TRadioOption[]>([])
89
+ const checkboxOptions = ref<TCheckboxOption[]>([])
90
+ const showFilterDropdown = ref<boolean | 'checkbox'>(false)
91
+
92
+ onMounted(() => {
93
+ document.addEventListener('click', clickListener)
94
+ document.addEventListener('keydown', keydownListener)
95
+ radioOptions.value = [ ...radioEntries ]
96
+ checkboxOptions.value = [ ...checkboxEntries ]
73
97
  })
74
98
 
75
- export default class FilterDropdown extends Vue {
76
- @Prop() radioEntries!: TRadioOption[]
77
- @Prop() checkboxEntries!: TCheckboxOption[]
78
- @Prop() filterLabel!: string
79
- @Prop({ default: 'right' }) align!: 'left' | 'right'
80
- @Prop({ default: 'small' }) size!: 'small' | 'large'
81
- @Prop({ default: false }) isDarkMode!: boolean
82
- @Prop({ default: false }) responsiveButton!: boolean
83
- @Prop({ default: 'FILTER BY SUBJECT' }) filterOptionsLabel?: string
84
-
85
- radioOptions: TRadioOption[] = []
86
- checkboxOptions: TCheckboxOption[] = []
87
-
88
- showFilterDropdown: boolean | 'checkbox' = false
89
-
90
- mounted () {
91
- document.addEventListener('click', this.clickListener)
92
- document.addEventListener('keydown', this.keydownListener)
93
- this.radioOptions = [ ...this.radioEntries ]
94
- this.checkboxOptions = [ ...this.checkboxEntries ]
95
- }
99
+ onBeforeUnmount(() => {
100
+ document.removeEventListener('click', clickListener)
101
+ document.removeEventListener('keydown', keydownListener)
102
+ })
96
103
 
97
- keydownListener (e: KeyboardEvent) {
98
- if (e.code === 'Escape') {
99
- e.stopPropagation()
100
- this.showFilterDropdown = false
101
- }
104
+ const keydownListener = (e: KeyboardEvent) => {
105
+ if (e.code === 'Escape') {
106
+ e.stopPropagation()
107
+ showFilterDropdown.value = false
102
108
  }
103
- clickListener (e: MouseEvent) {
104
- if (this.showFilterDropdown) {
105
- const targetEl = e.target as HTMLElement
106
- if (!targetEl?.closest('.uikit-filter')
107
- && this.showFilterDropdown !== 'checkbox'
108
- && document.body.contains(targetEl)
109
- ) {
110
- this.showFilterDropdown = false
111
- }
109
+ }
110
+ const clickListener = (e: MouseEvent) => {
111
+ if (showFilterDropdown.value) {
112
+ const targetEl = e.target as HTMLElement
113
+ if (!targetEl?.closest('.uikit-filter')
114
+ && showFilterDropdown.value !== 'checkbox'
115
+ && document.body.contains(targetEl)
116
+ ) {
117
+ showFilterDropdown.value = false
112
118
  }
113
119
  }
120
+ }
114
121
 
115
- beforeUnmount () {
116
- document.removeEventListener('click', this.clickListener)
117
- document.removeEventListener('keydown', this.keydownListener)
122
+ watch(checkboxOptions, (newVal: TCheckboxOption[], oldVal: TCheckboxOption[]) => {
123
+ const newEnabledFilters = newVal.filter(o => o.enabled)
124
+ const oldEnabledFilters = oldVal.filter(o => o.enabled)
125
+ if (newEnabledFilters.length !== oldEnabledFilters.length) {
126
+ emitUpdateModelValue(newVal)
118
127
  }
128
+ }, { deep: true })
119
129
 
120
- @Watch('checkboxOptions', { deep: true })
121
- checkboxOptionsChanged (newVal: TCheckboxOption[], oldVal: TCheckboxOption[]) {
122
- const newEnabledFilters = newVal.filter(o => o.enabled)
123
- const oldEnabledFilters = oldVal.filter(o => o.enabled)
124
- if (newEnabledFilters.length !== oldEnabledFilters.length) {
125
- this.emitUpdateModelValue(newVal)
126
- }
127
- }
128
- @Watch('checkboxEntries', { deep: true })
129
- checkboxOptionChanged (newVal: TCheckboxOption[]) {
130
- this.checkboxOptions = [ ...newVal ]
131
- }
130
+ watch(() => checkboxEntries, (newVal: TCheckboxOption[]) => {
131
+ checkboxOptions.value = [ ...newVal ]
132
+ }, { deep: true })
132
133
 
133
- @Watch('radioOptions', { deep: true })
134
- radioOptionsChanged (newVal: TRadioOption[], oldVal: TRadioOption[]) {
135
- const newEnabledFilters = newVal
136
- const oldEnabledFilters = oldVal
137
- if (newEnabledFilters.length !== oldEnabledFilters.length) {
138
- this.emitUpdateModelValue(newVal)
139
- }
140
- }
141
- @Watch('radioEntries', { deep: true })
142
- radioOptionChanged (newVal: TRadioOption[]) {
143
- this.radioOptions = [ ...newVal ]
134
+ watch(radioOptions, (newVal: TRadioOption[], oldVal: TRadioOption[]) => {
135
+ const newEnabledFilters = newVal
136
+ const oldEnabledFilters = oldVal
137
+ if (newEnabledFilters.length !== oldEnabledFilters.length) {
138
+ emitUpdateModelValue(newVal)
144
139
  }
140
+ }, { deep: true })
145
141
 
146
- @Emit('update:modelValue')
147
- emitUpdateModelValue (options: TCheckboxOption[] | TRadioOption[]) {
148
- return options
149
- }
150
- }
142
+ watch(() => radioEntries, (newVal: TRadioOption[]) => {
143
+ radioOptions.value = [ ...newVal ]
144
+ }, { deep: true })
151
145
 
146
+ const emitUpdateModelValue = (options: TCheckboxOption[] | TRadioOption[]) => {
147
+ emit('update:modelValue', options)
148
+ }
152
149
  </script>
153
150
 
154
151
  <style lang="scss">
@@ -218,4 +215,4 @@ export default class FilterDropdown extends Vue {
218
215
  }
219
216
  }
220
217
  }
221
- </style>
218
+ </style>
@@ -2,9 +2,9 @@
2
2
  <div
3
3
  v-dark="isDarkMode"
4
4
  class="filter-options"
5
- :class="{
6
- [`filter-options--${align}`]: true,
7
- [`filter-options-${size}`]: true
5
+ :class="{
6
+ [`filter-options--${align}`]: true,
7
+ [`filter-options-${size}`]: true
8
8
  }"
9
9
  >
10
10
  <slot name="radioLabel">
@@ -28,7 +28,7 @@
28
28
  />
29
29
  </slot>
30
30
  </div>
31
- </slot>
31
+ </slot>
32
32
  <slot name="radio">
33
33
  <Radio
34
34
  v-if="showRadioOptions"
@@ -93,108 +93,103 @@
93
93
  </div>
94
94
  </template>
95
95
 
96
- <script lang="ts">
97
- import { Component, Emit, Prop, Vue, Watch } from 'vue-facing-decorator'
96
+ <script setup lang="ts">
98
97
  import CheckboxOption from '../Forms/CheckboxOption.vue'
99
- import Link from '../Buttons/Link.vue'
100
98
  import Radio from '../Forms/Radio.vue'
101
99
  import Icon from '../Icons/Icon.vue'
102
- import { dark } from '../../directives'
100
+ import { dark as vDark } from '../../directives'
103
101
  import type { TRadioOption, TCheckboxOption } from './filterOptions'
102
+ import { onMounted, ref, watch } from 'vue'
104
103
 
105
- @Component({
106
- components: {
107
- CheckboxOption,
108
- Link,
109
- Radio,
110
- Icon,
111
- },
112
- directives: {
113
- dark,
114
- },
115
- })
104
+ const {
105
+ defaultRadioEntry = '',
106
+ radioEntries,
107
+ checkboxEntries,
108
+ align = 'right',
109
+ size = 'small',
110
+ isDarkMode = false,
111
+ filterOptionsLabel = 'FILTER BY SUBJECT',
112
+ } = defineProps<{
113
+ defaultRadioEntry?: string
114
+ radioEntries: TRadioOption[]
115
+ checkboxEntries: TCheckboxOption[]
116
+ align?: 'left' | 'right'
117
+ size?: 'small' | 'large'
118
+ isDarkMode?: boolean
119
+ filterOptionsLabel?: string
120
+ }>()
116
121
 
122
+ const emit = defineEmits<{
123
+ 'update:modelValue': [options: TCheckboxOption[] | TRadioOption[]]
124
+ }>()
117
125
 
118
- export default class FilterOptions extends Vue {
119
- @Prop({ default: '' }) defaultRadioEntry?: string
120
- @Prop() radioEntries!: TRadioOption[]
121
- @Prop() checkboxEntries!: TCheckboxOption[]
122
- @Prop({ default: 'right' }) align!: 'left' | 'right'
123
- @Prop({ default: 'small' }) size!: 'small' | 'large'
124
- @Prop({ default: false }) isDarkMode!: boolean
125
- @Prop({ default: 'FILTER BY SUBJECT' }) filterOptionsLabel?: string
126
+ const radioOptions = ref<TRadioOption[]>([])
127
+ const checkboxOptions = ref<TCheckboxOption[]>([])
126
128
 
127
- radioOptions: TRadioOption[] = []
128
- checkboxOptions: TCheckboxOption[] = []
129
+ const showFilterDropdown = ref<boolean | 'checkbox'>(false)
130
+ const showRadioOptions = ref(true)
131
+ const showCheckboxOptions = ref(false)
129
132
 
130
- showFilterDropdown: boolean | 'checkbox' = false
131
- showRadioOptions = true
132
- showCheckboxOptions = false
133
+ const radioEntry = ref<{ label: string; value: string } | null>(radioOptions.value.find(
134
+ o => o.value === defaultRadioEntry
135
+ ) || null)
133
136
 
134
- radioEntry: { label: string; value: string } | null = this.radioOptions.find(
135
- o => o.value === this.defaultRadioEntry
136
- ) || null
137
+ const stringHasSpaces = (val: string) => {
138
+ return val.includes(' ')
139
+ }
137
140
 
138
- stringHasSpaces (val: string) {
139
- return val.includes(' ')
141
+ const keydownListener = (e: KeyboardEvent) => {
142
+ if (e.code === 'Escape') {
143
+ e.stopPropagation()
144
+ showFilterDropdown.value = false
140
145
  }
141
-
142
- keydownListener (e: KeyboardEvent) {
143
- if (e.code === 'Escape') {
144
- e.stopPropagation()
145
- this.showFilterDropdown = false
146
- }
147
- }
148
- clickListener (e: MouseEvent) {
149
- if (this.showFilterDropdown) {
150
- const targetEl = e.target as HTMLElement
151
- if (!targetEl?.closest('.filter')
152
- && this.showFilterDropdown !== 'checkbox'
153
- && document.body.contains(targetEl)
154
- ) {
155
- this.showFilterDropdown = false
156
- }
146
+ }
147
+ const clickListener = (e: MouseEvent) => {
148
+ if (showFilterDropdown.value) {
149
+ const targetEl = e.target as HTMLElement
150
+ if (!targetEl?.closest('.filter')
151
+ && showFilterDropdown.value !== 'checkbox'
152
+ && document.body.contains(targetEl)
153
+ ) {
154
+ showFilterDropdown.value = false
157
155
  }
158
156
  }
157
+ }
159
158
 
160
- mounted () {
161
- document.addEventListener('click', this.clickListener)
162
- document.addEventListener('keydown', this.keydownListener)
163
- this.radioOptions = [ ...this.radioEntries ]
164
- this.checkboxOptions = [ ...this.checkboxEntries ]
165
- }
159
+ onMounted(() => {
160
+ document.addEventListener('click', clickListener)
161
+ document.addEventListener('keydown', keydownListener)
162
+ radioOptions.value = [ ...radioEntries ]
163
+ checkboxOptions.value = [ ...checkboxEntries ]
164
+ })
166
165
 
167
- @Watch('checkboxOptions', { deep: true })
168
- checkboxOptionsChanged (newVal: TCheckboxOption[], oldVal: TCheckboxOption[]) {
166
+ watch(checkboxOptions, (newVal: TCheckboxOption[], oldVal: TCheckboxOption[]) => {
169
167
 
170
- const newEnabledFilters = newVal.filter(o => o.enabled)
171
- const oldEnabledFilters = oldVal.filter(o => o.enabled)
172
- if (newEnabledFilters.length !== oldEnabledFilters.length) {
173
- this.emitUpdateModelValue(newVal)
174
- }
175
- }
176
- @Watch('checkboxEntries', { deep: true })
177
- checkboxOptionChanged (newVal: TCheckboxOption[]) {
178
- this.checkboxOptions = [ ...newVal ]
168
+ const newEnabledFilters = newVal.filter(o => o.enabled)
169
+ const oldEnabledFilters = oldVal.filter(o => o.enabled)
170
+ if (newEnabledFilters.length !== oldEnabledFilters.length) {
171
+ emitUpdateModelValue(newVal)
179
172
  }
173
+ }, { deep: true })
180
174
 
181
- @Watch('radioOptions', { deep: true })
182
- radioOptionsChanged (newVal: TRadioOption[], oldVal: TRadioOption[]) {
183
- const newEnabledFilters = newVal
184
- const oldEnabledFilters = oldVal
185
- if (newEnabledFilters.length !== oldEnabledFilters.length) {
186
- this.emitUpdateModelValue(newVal)
187
- }
188
- }
189
- @Watch('radioEntries', { deep: true })
190
- radioOptionChanged (newVal: TRadioOption[]) {
191
- this.radioOptions = [ ...newVal ]
192
- }
175
+ watch(() => checkboxEntries, (newVal: TCheckboxOption[]) => {
176
+ checkboxOptions.value = [ ...newVal ]
177
+ }, { deep: true })
193
178
 
194
- @Emit('update:modelValue')
195
- emitUpdateModelValue (options: TCheckboxOption[] | TRadioOption[]) {
196
- return options
179
+ watch(radioOptions, (newVal: TRadioOption[], oldVal: TRadioOption[]) => {
180
+ const newEnabledFilters = newVal
181
+ const oldEnabledFilters = oldVal
182
+ if (newEnabledFilters.length !== oldEnabledFilters.length) {
183
+ emitUpdateModelValue(newVal)
197
184
  }
185
+ }, { deep: true })
186
+
187
+ watch(() => radioEntries, (newVal: TRadioOption[]) => {
188
+ radioOptions.value = [ ...newVal ]
189
+ }, { deep: true })
190
+
191
+ const emitUpdateModelValue = (options: TCheckboxOption[] | TRadioOption[]) => {
192
+ emit('update:modelValue', options)
198
193
  }
199
194
 
200
195
  </script>
@@ -234,7 +229,7 @@ export default class FilterOptions extends Vue {
234
229
  }
235
230
 
236
231
  &:hover {
237
- svg,
232
+ svg,
238
233
  span {
239
234
  color: $brand-blue;
240
235
  cursor: pointer;
@@ -262,7 +257,7 @@ export default class FilterOptions extends Vue {
262
257
  }
263
258
 
264
259
  &:hover {
265
- svg,
260
+ svg,
266
261
  span {
267
262
  color: $banana-bread;
268
263
  cursor: pointer;
@@ -299,4 +294,4 @@ export default class FilterOptions extends Vue {
299
294
  }
300
295
  }
301
296
  }
302
- </style>
297
+ </style>