@asd20/ui-next 1.0.11 → 2.0.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 (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/package.json +3 -3
  3. package/src/components/atoms/Asd20Checkbox/index.vue +2 -4
  4. package/src/components/atoms/Asd20CountUp/index.vue +2 -3
  5. package/src/components/atoms/Asd20Icon/index.vue +6 -62
  6. package/src/components/atoms/Asd20MultiselectInput/index.vue +6 -1
  7. package/src/components/atoms/Asd20SelectInput/index.vue +3 -1
  8. package/src/components/atoms/Asd20WidgetViewport/index.vue +2 -3
  9. package/src/components/molecules/Asd20Accordion/index.vue +2 -3
  10. package/src/components/molecules/Asd20Card/index.vue +10 -5
  11. package/src/components/molecules/Asd20CheckboxList/index.vue +4 -13
  12. package/src/components/molecules/Asd20FileInput/index.vue +3 -2
  13. package/src/components/molecules/Asd20SearchField/index.vue +3 -5
  14. package/src/components/molecules/Asd20Share/index.vue +2 -3
  15. package/src/components/molecules/Asd20SliderInput/index.vue +3 -5
  16. package/src/components/molecules/Asd20Swipe/index.vue +2 -3
  17. package/src/components/molecules/Asd20Swiper/index.vue +2 -3
  18. package/src/components/molecules/Asd20TextAreaInput/index.vue +1 -3
  19. package/src/components/molecules/Asd20TextInput/index.vue +3 -1
  20. package/src/components/organisms/Asd20AiSearch/index.vue +2 -3
  21. package/src/components/organisms/Asd20SchoolHomepageVideoHeader/index.vue +2 -3
  22. package/src/components/organisms/Asd20SecondaryHeader/index.vue +2 -3
  23. package/src/components/organisms/Asd20SiteNavigation/index.vue +2 -3
  24. package/src/components/organisms/Asd20SiteSearch/index.vue +2 -3
  25. package/src/components/organisms/Asd20SwiperFeed/index.vue +1 -1
  26. package/src/components/organisms/Asd20TabBar/index.vue +2 -3
  27. package/src/components/organisms/Asd20VideoHeader/index.vue +2 -3
  28. package/src/components/templates/Asd20AppTemplate/index.vue +17 -10
  29. package/src/components/templates/Asd20ArticleDigestCompactTemplate/index.vue +4 -4
  30. package/src/components/templates/Asd20ArticleDigestTemplate/index.vue +5 -5
  31. package/src/components/templates/Asd20ArticleListTemplate/index.vue +4 -4
  32. package/src/components/templates/Asd20ClubsTemplate/index.vue +4 -4
  33. package/src/components/templates/Asd20SalaryCalculatorTemplate/index.vue +0 -2
  34. package/src/components/templates/Asd20SearchAppTemplate/index.vue +17 -10
  35. package/src/components/utils/FocusTrap.vue +2 -3
  36. package/src/components/utils/Intersect.vue +2 -3
  37. package/src/components/utils/MqLayout.vue +2 -3
  38. package/src/components/utils/Multiselect.vue +2 -3
  39. package/src/components/utils/Recaptcha.vue +2 -3
  40. package/src/directives/lazy-image.js +4 -2
  41. package/src/helpers/mapPageQueryResultToPageTemplateProps.js +2 -2
  42. package/src/mixins/inputComponentMixin.js +2 -7
  43. package/src/mixins/responsiveBreakpointMixin.js +2 -3
  44. package/src/utils/createLegacyDestroyHooks.js +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ # [2.0.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v1.0.11...ui-next-v2.0.0) (2026-03-27)
4
+
5
+
6
+ * feat!: finalize Vue 3-only shared input contract ([8a4652e](https://github.com/academydistrict20/asd20-ui-next/commit/8a4652eaa09378b86a574951fa3996c000b12bb8))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * @asd20/ui-next now publishes a Vue 3-only peer baseline and the shared input primitives no longer support the legacy value/input API. Asd20CheckboxList is now modelValue-only and no longer emits update:checkedValues.
12
+
3
13
  ## [1.0.11](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v1.0.10...ui-next-v1.0.11) (2026-03-26)
4
14
 
5
15
  ## [1.0.10](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v1.0.9...ui-next-v1.0.10) (2026-03-26)
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@asd20/ui-next",
3
- "version": "1.0.11",
3
+ "version": "2.0.0",
4
4
  "private": false,
5
- "description": "ASD20 UI component library migration workspace.",
5
+ "description": "ASD20 UI component library for Vue 3.",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -45,6 +45,6 @@
45
45
  "uuid": "^3.3.2"
46
46
  },
47
47
  "peerDependencies": {
48
- "vue": "^2.6.14 || ^3.0.0"
48
+ "vue": "^3.0.0"
49
49
  }
50
50
  }
@@ -40,8 +40,7 @@ export default {
40
40
  name: 'Asd20CheckboxIcon',
41
41
  components: { Asd20Badge },
42
42
  props: {
43
- value: { type: Boolean, default: false },
44
- modelValue: { type: Boolean, default: undefined },
43
+ modelValue: { type: Boolean, default: false },
45
44
  label: { type: String, default: '' },
46
45
  description: { type: String, default: '' },
47
46
  count: { type: Number, default: 0 },
@@ -57,7 +56,7 @@ export default {
57
56
  },
58
57
  computed: {
59
58
  resolvedValue() {
60
- return typeof this.modelValue === 'boolean' ? this.modelValue : this.value
59
+ return this.modelValue
61
60
  },
62
61
  checkboxAttrs() {
63
62
  return {
@@ -83,7 +82,6 @@ export default {
83
82
  methods: {
84
83
  toggle() {
85
84
  const nextValue = !this.resolvedValue
86
- this.$emit('input', nextValue)
87
85
  this.$emit('update:modelValue', nextValue)
88
86
  this.toggleIcon = nextValue
89
87
  },
@@ -5,7 +5,6 @@
5
5
  <script>
6
6
  import _isFunction from 'lodash/isFunction'
7
7
  import { CountUp } from 'countup.js'
8
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
9
8
 
10
9
  export default {
11
10
  __countup__: CountUp,
@@ -35,9 +34,9 @@ export default {
35
34
  mounted() {
36
35
  this.init()
37
36
  },
38
- ...createLegacyDestroyHooks(function() {
37
+ beforeUnmount() {
39
38
  this.uninit()
40
- }),
39
+ },
41
40
  methods: {
42
41
  init() {
43
42
  if (this.instance) {
@@ -18,6 +18,7 @@
18
18
  </template>
19
19
 
20
20
  <script>
21
+ import { defineAsyncComponent, markRaw } from 'vue'
21
22
  import camelCase from 'lodash/camelCase'
22
23
  import iconLoaders from '../icons/registry'
23
24
  import createComponentInstanceId from '../../../utils/createComponentInstanceId'
@@ -40,64 +41,11 @@ function normalizeIconName(name) {
40
41
  .toLowerCase()
41
42
  }
42
43
 
43
- function resolveDefineAsyncComponent() {
44
- let vueRuntime = {}
45
-
46
- try {
47
- vueRuntime = require('vue')
48
- } catch {
49
- return null
50
- }
51
-
52
- if (typeof vueRuntime.defineAsyncComponent === 'function') {
53
- return vueRuntime.defineAsyncComponent
54
- }
55
-
56
- if (
57
- vueRuntime.default &&
58
- typeof vueRuntime.default.defineAsyncComponent === 'function'
59
- ) {
60
- return vueRuntime.default.defineAsyncComponent
61
- }
62
-
63
- return null
64
- }
65
-
66
- function resolveMarkRaw() {
67
- let vueRuntime = {}
68
-
69
- try {
70
- vueRuntime = require('vue')
71
- } catch {
72
- return null
73
- }
74
-
75
- if (typeof vueRuntime.markRaw === 'function') {
76
- return vueRuntime.markRaw
77
- }
78
-
79
- if (
80
- vueRuntime.default &&
81
- typeof vueRuntime.default.markRaw === 'function'
82
- ) {
83
- return vueRuntime.default.markRaw
84
- }
85
-
86
- return null
87
- }
88
-
89
44
  function createAsyncComponent(loader) {
90
- const defineAsyncComponent = resolveDefineAsyncComponent()
91
-
92
- if (typeof defineAsyncComponent === 'function') {
93
- return defineAsyncComponent({
94
- loader,
95
- suspensible: false,
96
- })
97
- }
98
-
99
- // Vue 2 accepts the loader function directly for async components.
100
- return loader
45
+ return defineAsyncComponent({
46
+ loader,
47
+ suspensible: false,
48
+ })
101
49
  }
102
50
 
103
51
  export default {
@@ -174,11 +122,7 @@ export default {
174
122
  return
175
123
  }
176
124
 
177
- const asyncComponent = createAsyncComponent(this.loader)
178
- const markRaw = resolveMarkRaw()
179
-
180
- this.iconTemplate =
181
- typeof markRaw === 'function' ? markRaw(asyncComponent) : asyncComponent
125
+ this.iconTemplate = markRaw(createAsyncComponent(this.loader))
182
126
  },
183
127
  },
184
128
  }
@@ -89,6 +89,12 @@ export default {
89
89
  name: 'Asd20MultiselectInput',
90
90
  components: { Asd20Icon, Multiselect },
91
91
  mixins: [inputComponentMixin],
92
+ props: {
93
+ modelValue: {
94
+ type: [Array, Object, String, Number, Boolean],
95
+ default: () => [],
96
+ },
97
+ },
92
98
  data: () => ({
93
99
  taggedItems: [],
94
100
  }),
@@ -148,7 +154,6 @@ export default {
148
154
  },
149
155
  methods: {
150
156
  emitValue(value) {
151
- this.$emit('input', value)
152
157
  this.$emit('update:modelValue', value)
153
158
  },
154
159
  getItemKey(item) {
@@ -14,7 +14,6 @@
14
14
  <div class="asd20-select-input__select-wrapper">
15
15
  <select
16
16
  v-bind="mergedInputAttrs"
17
- v-on="legacyListeners"
18
17
  @input="input"
19
18
  >
20
19
  <option
@@ -59,6 +58,9 @@ export default {
59
58
  name: 'Asd20SelectInput',
60
59
  components: { Asd20Icon },
61
60
  mixins: [inputComponentMixin],
61
+ props: {
62
+ modelValue: { default: '' },
63
+ },
62
64
  computed: {
63
65
  mergedInputAttrs() {
64
66
  return this.mergeInputAttrs({
@@ -37,7 +37,6 @@
37
37
  </template>
38
38
 
39
39
  <script>
40
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
41
40
 
42
41
  export default {
43
42
  name: 'Asd20Viewport',
@@ -83,9 +82,9 @@ export default {
83
82
  this.checkForOverflow()
84
83
  window.addEventListener('resize', this.checkForOverflow)
85
84
  },
86
- ...createLegacyDestroyHooks(function() {
85
+ beforeUnmount() {
87
86
  window.removeEventListener('resize', this.checkForOverflow)
88
- }),
87
+ },
89
88
  methods: {
90
89
  checkForOverflow() {
91
90
  const viewport = this.$refs.viewport
@@ -71,7 +71,6 @@ import Asd20Badge from '../../atoms/Asd20Badge'
71
71
  import Asd20Button from '../../atoms/Asd20Button'
72
72
  import Asd20Viewport from '../../atoms/Asd20Viewport'
73
73
  import createComponentInstanceId from '../../../utils/createComponentInstanceId'
74
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
75
74
 
76
75
  const FOCUSABLE_SELECTOR = [
77
76
  'a[href]',
@@ -125,9 +124,9 @@ export default {
125
124
  updated() {
126
125
  this.syncCollapsedContentInteractivity()
127
126
  },
128
- ...createLegacyDestroyHooks(function() {
127
+ beforeUnmount() {
129
128
  this.restoreCollapsedContentInteractivity()
130
- }),
129
+ },
131
130
  methods: {
132
131
  toggleOpen: function() {
133
132
  this.open = !this.open
@@ -292,6 +292,7 @@ export default {
292
292
  reversed: { type: Boolean, default: false },
293
293
  pinned: { type: Boolean, default: false },
294
294
  zoom: { type: Boolean, default: false },
295
+ separateImage: { type: Boolean, default: false },
295
296
  seperateImage: { type: Boolean, default: false },
296
297
  expandedHeader: { type: Boolean, default: false },
297
298
  expandedDescription: { type: Boolean, default: false },
@@ -316,6 +317,8 @@ export default {
316
317
  return truncate(strippedDescription, { length: 150, separator: /,? +/ })
317
318
  },
318
319
  classes() {
320
+ const hasSeparateImage = this.separateImage || this.seperateImage
321
+
319
322
  return {
320
323
  'asd20-card': true,
321
324
  'asd20-card--without-image': !this.image || this.noCoverImage,
@@ -323,7 +326,8 @@ export default {
323
326
  'asd20-card--selected': this.selected,
324
327
  'asd20-card--reversed': this.reversed,
325
328
  'asd20-card--zoom': this.zoom,
326
- 'asd20-card--seperate-image': this.seperateImage,
329
+ 'asd20-card--separate-image': hasSeparateImage,
330
+ 'asd20-card--seperate-image': hasSeparateImage,
327
331
  'asd20-card--pinned': this.pinned,
328
332
  'asd20-card--emphasized-date': this.emphasizedDate,
329
333
  'asd20-card--expanded-header': this.expandedHeader,
@@ -801,7 +805,8 @@ export default {
801
805
  }
802
806
  }
803
807
 
804
- &--seperate-image:not(.asd20-card--without-image) {
808
+ &--seperate-image:not(.asd20-card--without-image),
809
+ &--separate-image:not(.asd20-card--without-image) {
805
810
  transform: rotate(0deg);
806
811
  justify-content: flex-start;
807
812
  background: var(--website-card__background-color);
@@ -835,14 +840,14 @@ export default {
835
840
  }
836
841
  }
837
842
 
838
- &--with-image:not(.asd20-card--seperate-image),
843
+ &--with-image:not(.asd20-card--seperate-image):not(.asd20-card--separate-image),
839
844
  &--without-image.asd20-card--reversed {
840
845
  .asd20-card__title {
841
846
  color: var(--website-card__reverse-foreground-color);
842
847
  }
843
848
  }
844
849
 
845
- &--with-image:not(.asd20-card--seperate-image) {
850
+ &--with-image:not(.asd20-card--seperate-image):not(.asd20-card--separate-image) {
846
851
  min-height: 30vh;
847
852
  .asd20-card__content {
848
853
  background: linear-gradient(
@@ -854,7 +859,7 @@ export default {
854
859
  }
855
860
 
856
861
  &--reversed,
857
- &--with-image:not(.asd20-card--seperate-image) {
862
+ &--with-image:not(.asd20-card--seperate-image):not(.asd20-card--separate-image) {
858
863
  color: var(--website-card__reverse-foreground-color);
859
864
  }
860
865
  &--reversed {
@@ -10,9 +10,9 @@
10
10
  :label="option.text"
11
11
  :count="option.count"
12
12
  :description="option.description"
13
- :value="resolvedCheckedValues.indexOf(option.value) > -1"
13
+ :model-value="resolvedCheckedValues.indexOf(option.value) > -1"
14
14
  :radio="radio"
15
- @input="onInput($event, option)"
15
+ @update:modelValue="onInput($event, option)"
16
16
  />
17
17
  <slot
18
18
  name="item"
@@ -29,20 +29,14 @@ import Asd20Checkbox from '../../atoms/Asd20Checkbox'
29
29
  export default {
30
30
  name: 'Asd20CheckboxList',
31
31
  components: { Asd20Checkbox },
32
- model: {
33
- prop: 'checkedValues',
34
- },
35
32
  props: {
36
- checkedValues: { type: Array, default: () => [] },
37
- modelValue: { type: Array, default: undefined },
33
+ modelValue: { type: Array, default: () => [] },
38
34
  options: { type: Array, default: () => [] },
39
35
  radio: { type: Boolean, default: false },
40
36
  },
41
37
  computed: {
42
38
  resolvedCheckedValues() {
43
- return Array.isArray(this.modelValue)
44
- ? this.modelValue
45
- : this.checkedValues
39
+ return this.modelValue
46
40
  },
47
41
  },
48
42
  methods: {
@@ -57,9 +51,6 @@ export default {
57
51
  )
58
52
  }
59
53
  this.$emit('change', { option, checked })
60
-
61
- this.$emit('input', modifiedCheckedValues, option, checked)
62
- this.$emit('update:checkedValues', modifiedCheckedValues)
63
54
  this.$emit('update:modelValue', modifiedCheckedValues)
64
55
  },
65
56
  },
@@ -14,7 +14,6 @@
14
14
 
15
15
  <input
16
16
  v-bind="mergedInputAttrs"
17
- v-on="legacyListeners"
18
17
  @input="input"
19
18
  />
20
19
 
@@ -43,6 +42,9 @@ export default {
43
42
  components: { Asd20Icon },
44
43
  mixins: [inputComponentMixin],
45
44
  inheritAttrs: false,
45
+ props: {
46
+ modelValue: { default: null },
47
+ },
46
48
  computed: {
47
49
  mergedInputAttrs() {
48
50
  return this.mergeInputAttrs({
@@ -56,7 +58,6 @@ export default {
56
58
  input(event) {
57
59
  this.isDirty = true
58
60
  this.validate(event.target.value)
59
- this.$emit('input', event.target.files)
60
61
  this.$emit('update:modelValue', event.target.files)
61
62
  },
62
63
  },
@@ -43,14 +43,13 @@ export default {
43
43
  name: 'Asd20SearchField',
44
44
  components: { Asd20Icon },
45
45
  props: {
46
- value: { type: String, default: '' },
47
- modelValue: { type: String, default: undefined },
46
+ modelValue: { type: String, default: '' },
48
47
  extra: { type: String, default: '' },
49
48
  idTag: { type: String, default: '' },
50
49
  large: { type: Boolean, default: false },
51
50
  placeholder: { type: String, default: 'Search' },
52
51
  },
53
- emits: ['input', 'update:modelValue', 'click', 'focusin', 'keyup'],
52
+ emits: ['update:modelValue', 'click', 'focusin', 'keyup'],
54
53
  computed: {
55
54
  classes() {
56
55
  return {
@@ -58,13 +57,12 @@ export default {
58
57
  }
59
58
  },
60
59
  resolvedValue() {
61
- return this.modelValue !== undefined ? this.modelValue : this.value
60
+ return this.modelValue
62
61
  },
63
62
  },
64
63
  methods: {
65
64
  input(event) {
66
65
  const value = event.target.value
67
- this.$emit('input', value)
68
66
  this.$emit('update:modelValue', value)
69
67
  },
70
68
  },
@@ -60,7 +60,6 @@
60
60
 
61
61
  <script>
62
62
  import Asd20Button from '../../atoms/Asd20Button'
63
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
64
63
  export default {
65
64
  name: 'Asd20Share',
66
65
 
@@ -79,14 +78,14 @@ export default {
79
78
  document.addEventListener('click', this.onDocumentClick)
80
79
  }
81
80
  },
82
- ...createLegacyDestroyHooks(function() {
81
+ beforeUnmount() {
83
82
  if (typeof document !== 'undefined') {
84
83
  document.removeEventListener('click', this.onDocumentClick)
85
84
  }
86
85
  if (this.toastTimeoutId) {
87
86
  clearTimeout(this.toastTimeoutId)
88
87
  }
89
- }),
88
+ },
90
89
 
91
90
  methods: {
92
91
  share() {
@@ -27,18 +27,17 @@ export default {
27
27
 
28
28
  props: {
29
29
  label: { type: String, default: '' },
30
- value: { type: Number, default: 50 },
31
- modelValue: { type: Number, default: undefined },
30
+ modelValue: { type: Number, default: 50 },
32
31
  progressColor: { type: String, default: '' },
33
32
  min: { type: Number, default: 0 },
34
33
  max: { type: Number, default: 100 },
35
34
  step: { type: Number, default: 1 },
36
35
  },
37
- emits: ['input', 'update:modelValue'],
36
+ emits: ['update:modelValue'],
38
37
 
39
38
  computed: {
40
39
  resolvedValue() {
41
- return typeof this.modelValue === 'number' ? this.modelValue : this.value
40
+ return this.modelValue
42
41
  },
43
42
  percentage() {
44
43
  if (this.max === 0) return '0%'
@@ -48,7 +47,6 @@ export default {
48
47
  methods: {
49
48
  input(event) {
50
49
  const value = Number(event.target.value)
51
- this.$emit('input', value)
52
50
  this.$emit('update:modelValue', value)
53
51
  },
54
52
  },
@@ -65,7 +65,6 @@
65
65
  </template>
66
66
 
67
67
  <script>
68
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
69
68
 
70
69
  export default {
71
70
  name: 'Asd20Swipe',
@@ -140,9 +139,9 @@ export default {
140
139
  mounted() {
141
140
  this.init()
142
141
  },
143
- ...createLegacyDestroyHooks(function() {
142
+ beforeUnmount() {
144
143
  this.clearAutoplayTimer()
145
- }),
144
+ },
146
145
 
147
146
  methods: {
148
147
  clearAutoplayTimer() {
@@ -48,7 +48,6 @@ import {
48
48
  } from 'swiper/js/swiper.esm'
49
49
  import merge from 'lodash/merge'
50
50
  import 'swiper/css/swiper.css'
51
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
52
51
 
53
52
  // see http://idangero.us/swiper/api/#custom-build
54
53
  Swiper.use([A11y, Keyboard, Pagination, Navigation, Mousewheel])
@@ -85,9 +84,9 @@ export default {
85
84
  updated() {
86
85
  this.swiper.update()
87
86
  },
88
- ...createLegacyDestroyHooks(function() {
87
+ beforeUnmount() {
89
88
  this.destroySwiper()
90
- }),
89
+ },
91
90
 
92
91
  methods: {
93
92
  destroySwiper() {
@@ -18,7 +18,6 @@
18
18
 
19
19
  <textarea
20
20
  v-bind="mergedInputAttrs"
21
- v-on="legacyListeners"
22
21
  @input="input"
23
22
  />
24
23
 
@@ -48,8 +47,7 @@ export default {
48
47
  mixins: [inputComponentMixin],
49
48
  inheritAttrs: false,
50
49
  props: {
51
- value: { type: String, default: '' },
52
- label: { type: String, default: '' },
50
+ modelValue: { type: String, default: '' },
53
51
  },
54
52
  computed: {
55
53
  mergedInputAttrs() {
@@ -14,7 +14,6 @@
14
14
 
15
15
  <input
16
16
  v-bind="mergedInputAttrs"
17
- v-on="legacyListeners"
18
17
  @input="input"
19
18
  />
20
19
 
@@ -43,6 +42,9 @@ export default {
43
42
  components: { Asd20Icon },
44
43
  mixins: [inputComponentMixin],
45
44
  inheritAttrs: false,
45
+ props: {
46
+ modelValue: { type: String, default: '' },
47
+ },
46
48
  computed: {
47
49
  mergedInputAttrs() {
48
50
  return this.mergeInputAttrs({
@@ -103,7 +103,6 @@ import Asd20SearchField from '../../molecules/Asd20SearchField'
103
103
  import Asd20SiteSearch from '../Asd20SiteSearch'
104
104
  import Asd20Icon from '../../atoms/Asd20Icon'
105
105
  import Asd20Button from '../../atoms/Asd20Button'
106
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
107
106
 
108
107
  const SEARCH_SESSION_STORAGE_KEY = 'asd20-site-search-state-v1'
109
108
  const SEARCH_STATE_SYNC_EVENT = 'asd20-site-search:state-sync'
@@ -187,9 +186,9 @@ export default {
187
186
  window.addEventListener('focus', this.onWindowFocus, { passive: true })
188
187
  }
189
188
  },
190
- ...createLegacyDestroyHooks(function() {
189
+ beforeUnmount() {
191
190
  this.teardownWindowBindings()
192
- }),
191
+ },
193
192
  methods: {
194
193
  teardownWindowBindings() {
195
194
  if (typeof document !== 'undefined') {
@@ -105,7 +105,6 @@
105
105
  import scrollTrack from '../../../directives/scroll-track'
106
106
  import responsiveBreakpointMixin from '../../../mixins/responsiveBreakpointMixin'
107
107
  import Asd20Button from '../../atoms/Asd20Button'
108
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
109
108
 
110
109
  export default {
111
110
  name: 'Asd20SchoolHomepageVideoHeader',
@@ -160,9 +159,9 @@ export default {
160
159
  this.checkIsMobile()
161
160
  window.addEventListener('resize', this.checkIsMobile)
162
161
  },
163
- ...createLegacyDestroyHooks(function() {
162
+ beforeUnmount() {
164
163
  this.teardownResizeListener()
165
- }),
164
+ },
166
165
  methods: {
167
166
  teardownResizeListener() {
168
167
  window.removeEventListener('resize', this.checkIsMobile)
@@ -126,7 +126,6 @@
126
126
 
127
127
  <script>
128
128
  import Asd20Messaging from '../../atoms/Asd20Messaging'
129
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
130
129
 
131
130
  export default {
132
131
  name: 'Asd20SecondaryHeader',
@@ -220,9 +219,9 @@ export default {
220
219
  // Initial set
221
220
  this.scheduleParallaxUpdate()
222
221
  },
223
- ...createLegacyDestroyHooks(function() {
222
+ beforeUnmount() {
224
223
  this.teardownParallax()
225
- }),
224
+ },
226
225
  methods: {
227
226
  teardownParallax() {
228
227
  this.disableParallax()
@@ -86,7 +86,6 @@ import Asd20Navbar from '../../../components/organisms/Asd20Navbar'
86
86
  import responsiveBreakpointMixin from '../../../mixins/responsiveBreakpointMixin'
87
87
  // import Asd20DistrictLogo from '../../../components/atoms/Asd20DistrictLogo'
88
88
  import Asd20OrganizationPicker from '../../../components/organisms/Asd20OrganizationPicker'
89
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
90
89
 
91
90
  export default {
92
91
  name: 'Asd20SiteNavigation',
@@ -136,9 +135,9 @@ export default {
136
135
  this.isLoaded = true
137
136
  window.addEventListener('resize', this.handleResize)
138
137
  },
139
- ...createLegacyDestroyHooks(function() {
138
+ beforeUnmount() {
140
139
  window.removeEventListener('resize', this.handleResize)
141
- }),
140
+ },
142
141
  methods: {
143
142
  onKeyboardToggleEvent(event) {
144
143
  this.keyboardToggleEvent = event
@@ -558,7 +558,6 @@ import queryAiSite from '../../../helpers/queryAiSite'
558
558
 
559
559
  // Mixins
560
560
  import globalPropMixinFactory from '../../../mixins/globalPropMixinFactory.js'
561
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
562
561
 
563
562
  const DISTRICT_ORG_ID = '26eaf390-d8ab-11e9-a3a8-5de5bba4f125'
564
563
  const SEARCH_ROUTE_QUERY_KEYS = {
@@ -1193,9 +1192,9 @@ export default {
1193
1192
  }
1194
1193
  },
1195
1194
 
1196
- ...createLegacyDestroyHooks(function() {
1195
+ beforeUnmount() {
1197
1196
  this.teardownSearchLifecycle()
1198
- }),
1197
+ },
1199
1198
 
1200
1199
  methods: {
1201
1200
  teardownSearchLifecycle() {
@@ -29,7 +29,7 @@
29
29
  <asd20-card
30
30
  v-bind="card"
31
31
  :card-type="cardType"
32
- seperate-image
32
+ separate-image
33
33
  @click="$emit('select', card)"
34
34
  @keydown.tab.exact="nextCard"
35
35
  @keydown.tab.shift="prevCard"
@@ -33,7 +33,6 @@
33
33
 
34
34
  <script>
35
35
  import Asd20Tab from '../../../components/molecules/Asd20Tab'
36
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
37
36
  export default {
38
37
  name: 'Asd20TabBar',
39
38
 
@@ -69,11 +68,11 @@ export default {
69
68
  window.addEventListener('resize', this.onResize, { passive: true })
70
69
  }
71
70
  },
72
- ...createLegacyDestroyHooks(function() {
71
+ beforeUnmount() {
73
72
  if (typeof window !== 'undefined') {
74
73
  window.removeEventListener('resize', this.onResize)
75
74
  }
76
- }),
75
+ },
77
76
 
78
77
  methods: {
79
78
  onScroll() {
@@ -151,7 +151,6 @@
151
151
 
152
152
  <script>
153
153
  import scrollTrack from '../../../directives/scroll-track'
154
- import createLegacyDestroyHooks from '../../../utils/createLegacyDestroyHooks'
155
154
  // import Asd20Button from '../../atoms/Asd20Button'
156
155
 
157
156
  export default {
@@ -202,9 +201,9 @@ export default {
202
201
  this.checkIsMobile()
203
202
  window.addEventListener('resize', this.checkIsMobile)
204
203
  },
205
- ...createLegacyDestroyHooks(function() {
204
+ beforeUnmount() {
206
205
  this.teardownResizeListener()
207
- }),
206
+ },
208
207
  methods: {
209
208
  teardownResizeListener() {
210
209
  window.removeEventListener('resize', this.checkIsMobile)
@@ -48,9 +48,9 @@
48
48
  <slot name="toolbar-start"></slot>
49
49
  <asd20-search-field
50
50
  v-if="searchable"
51
- :value="keywords"
51
+ :model-value="keywords"
52
52
  :placeholder="searchPlaceholder"
53
- @input="$emit('update:keywords', $event)"
53
+ @update:modelValue="$emit('update:keywords', $event)"
54
54
  />
55
55
  <asd20-button
56
56
  v-if="
@@ -80,9 +80,9 @@
80
80
  <slot name="tabs" />
81
81
  </asd20-tab-bar>
82
82
 
83
- <mq-layout
83
+ <div
84
+ v-if="isDesktopMq"
84
85
  class="asd20-app-template__desktop-layout"
85
- mq="lg+"
86
86
  >
87
87
  <div
88
88
  v-if="$slots.sidebar"
@@ -117,10 +117,10 @@
117
117
  </client-only>
118
118
  <slot />
119
119
  </div>
120
- </mq-layout>
120
+ </div>
121
121
 
122
- <mq-layout
123
- :mq="['sm', 'md']"
122
+ <div
123
+ v-if="isMobileMq"
124
124
  class="asd20-app-template__mobile-layout"
125
125
  >
126
126
  <asd20-modal
@@ -146,7 +146,7 @@
146
146
  <div class="asd20-app-template__main">
147
147
  <slot />
148
148
  </div>
149
- </mq-layout>
149
+ </div>
150
150
  <client-only>
151
151
  <asd20-notification-group
152
152
  :notifications="activeNotificationsByType.floating"
@@ -160,6 +160,7 @@
160
160
 
161
161
  <script>
162
162
  import responsiveBreakpointMixin from '../../../mixins/responsiveBreakpointMixin'
163
+ import { matchesMqQuery } from '../../../utils/responsive-mq'
163
164
  import Asd20AppHeader from '../../organisms/Asd20AppHeader'
164
165
  import Asd20Button from '../../atoms/Asd20Button'
165
166
  import Asd20Icon from '../../atoms/Asd20Icon'
@@ -168,7 +169,6 @@ import Asd20SearchField from '../../molecules/Asd20SearchField'
168
169
  import Asd20TabBar from '../../organisms/Asd20TabBar'
169
170
  import Asd20Badge from '../../atoms/Asd20Badge'
170
171
  import Asd20NotificationGroup from '../../organisms/Asd20NotificationGroup'
171
- import MqLayout from '../../utils/MqLayout'
172
172
 
173
173
  export default {
174
174
  name: 'Asd20AppTemplate',
@@ -181,7 +181,6 @@ export default {
181
181
  Asd20TabBar,
182
182
  Asd20Badge,
183
183
  Asd20NotificationGroup,
184
- MqLayout,
185
184
  },
186
185
  mixins: [responsiveBreakpointMixin],
187
186
 
@@ -212,6 +211,14 @@ export default {
212
211
  sidebarOpen: false,
213
212
  zoomed: false,
214
213
  }),
214
+ computed: {
215
+ isDesktopMq() {
216
+ return matchesMqQuery(this.mq, 'lg+')
217
+ },
218
+ isMobileMq() {
219
+ return matchesMqQuery(this.mq, ['sm', 'md'])
220
+ },
221
+ },
215
222
  mounted() {
216
223
  this.zoomed = window.innerHeight <= 500
217
224
  window.addEventListener('resize', this.handleResize)
@@ -85,16 +85,16 @@
85
85
  </h2>
86
86
  <!-- <div class="search-header">
87
87
  <asd20-search-field
88
- :value="keywords"
89
- @input="$emit('update:keywords', $event)"
88
+ :model-value="keywords"
89
+ @update:modelValue="$emit('update:keywords', $event)"
90
90
  medium
91
91
  />
92
92
  <asd20-multiselect-input
93
93
  label="Categories"
94
94
  :taggable="false"
95
- :value="selectedCategories"
95
+ :model-value="selectedCategories"
96
96
  :items="categoryOptions"
97
- @input="$emit('update:selected-categories', $event)"
97
+ @update:modelValue="$emit('update:selected-categories', $event)"
98
98
  :hideLabel="true"
99
99
  placeholder="Filter by Category"
100
100
  />
@@ -85,18 +85,18 @@
85
85
  </h2>
86
86
  <div class="search-header">
87
87
  <asd20-search-field
88
- :value="keywords"
88
+ :model-value="keywords"
89
89
  medium
90
- @input="$emit('update:keywords', $event)"
90
+ @update:modelValue="$emit('update:keywords', $event)"
91
91
  />
92
92
  <asd20-multiselect-input
93
93
  label="Categories"
94
94
  :taggable="false"
95
- :value="selectedCategories"
95
+ :model-value="selectedCategories"
96
96
  :items="categoryOptions"
97
97
  :hide-label="true"
98
98
  placeholder="Filter by Category"
99
- @input="$emit('update:selected-categories', $event)"
99
+ @update:modelValue="$emit('update:selected-categories', $event)"
100
100
  />
101
101
  </div>
102
102
 
@@ -159,7 +159,7 @@
159
159
  :key="index"
160
160
  v-bind="card"
161
161
  zoom
162
- seperate-image
162
+ separate-image
163
163
  ></asd20-card>
164
164
  </div>
165
165
  <div
@@ -96,19 +96,19 @@
96
96
  </h2>
97
97
  <div class="search-header">
98
98
  <asd20-search-field
99
- :value="keywords"
99
+ :model-value="keywords"
100
100
  medium
101
- @input="$emit('update:keywords', $event)"
101
+ @update:modelValue="$emit('update:keywords', $event)"
102
102
  />
103
103
  <asd20-multiselect-input
104
104
  label="Categories"
105
105
  :taggable="false"
106
- :value="selectedCategories"
106
+ :model-value="selectedCategories"
107
107
  :items="categoryOptions"
108
108
  :hide-label="true"
109
109
  placeholder="Filter by Category"
110
110
  autocomplete="off"
111
- @input="onCategorySelect"
111
+ @update:modelValue="onCategorySelect"
112
112
  />
113
113
  </div>
114
114
  <div v-if="cards.length < 1">
@@ -103,16 +103,16 @@
103
103
  </div>
104
104
  <div class="search-header">
105
105
  <asd20-search-field
106
- :value="keywords"
106
+ :model-value="keywords"
107
107
  medium
108
- @input="$emit('update:keywords', $event)"
108
+ @update:modelValue="$emit('update:keywords', $event)"
109
109
  />
110
110
  <!-- <asd20-multiselect-input
111
111
  label="Categories"
112
112
  :taggable="false"
113
- :value="selectedCategories"
113
+ :model-value="selectedCategories"
114
114
  :items="categoryOptions"
115
- @input="$emit('update:selected-categories', $event)"
115
+ @update:modelValue="$emit('update:selected-categories', $event)"
116
116
  /> -->
117
117
  </div>
118
118
 
@@ -95,7 +95,6 @@
95
95
  class="years-selector"
96
96
  label="Years of Experience"
97
97
  icon="calendar"
98
- :value="years"
99
98
  :items="yearsOfExperience"
100
99
  />
101
100
  <p class="selectionHeading">
@@ -106,7 +105,6 @@
106
105
  class="education-selector"
107
106
  label="Education Level"
108
107
  icon="school"
109
- :value="educationLevel"
110
108
  :items="educationLevelOptions"
111
109
  />
112
110
  <asd20-button
@@ -4,8 +4,8 @@
4
4
  <slot name="toolbar-start"></slot>
5
5
  <asd20-search-field
6
6
  v-if="searchable"
7
- :value="keywords"
8
- @input="$emit('update:keywords', $event)"
7
+ :model-value="keywords"
8
+ @update:modelValue="$emit('update:keywords', $event)"
9
9
  />
10
10
  <asd20-button
11
11
  v-if="$slots.filters && (mq === 'sm' || mq === 'md')"
@@ -24,9 +24,9 @@
24
24
  <slot name="toolbar-end"></slot>
25
25
  </div>
26
26
 
27
- <mq-layout
27
+ <div
28
+ v-if="isDesktopMq"
28
29
  class="asd20-search-app-template__desktop-layout"
29
- mq="lg+"
30
30
  >
31
31
  <aside v-if="$slots.filters || $slots.aside">
32
32
  <h2 v-if="$slots.filters">
@@ -59,10 +59,10 @@
59
59
  <main v-if="$slots.results">
60
60
  <slot name="results" />
61
61
  </main>
62
- </mq-layout>
62
+ </div>
63
63
 
64
- <mq-layout
65
- :mq="['sm', 'md']"
64
+ <div
65
+ v-if="isMobileMq"
66
66
  class="asd20-search-app-template__mobile-layout"
67
67
  >
68
68
  <asd20-modal
@@ -96,19 +96,19 @@
96
96
  </asd20-modal>
97
97
 
98
98
  <slot name="results" />
99
- </mq-layout>
99
+ </div>
100
100
  </div>
101
101
  </template>
102
102
 
103
103
  <script>
104
104
  import responsiveBreakpointMixin from '../../../mixins/responsiveBreakpointMixin'
105
+ import { matchesMqQuery } from '../../../utils/responsive-mq'
105
106
  import Asd20Viewport from '../../atoms/Asd20Viewport'
106
107
  import Asd20Modal from '../../molecules/Asd20Modal'
107
108
  import Asd20SearchField from '../../molecules/Asd20SearchField'
108
109
  import Asd20Badge from '../../atoms/Asd20Badge'
109
110
  import Asd20Icon from '../../atoms/Asd20Icon'
110
111
  import Asd20Button from '../../atoms/Asd20Button'
111
- import MqLayout from '../../utils/MqLayout'
112
112
 
113
113
  export default {
114
114
  name: 'Asd20SearchAppTemplate',
@@ -119,7 +119,6 @@ export default {
119
119
  Asd20Badge,
120
120
  Asd20Icon,
121
121
  Asd20Button,
122
- MqLayout,
123
122
  },
124
123
  mixins: [responsiveBreakpointMixin],
125
124
 
@@ -135,6 +134,14 @@ export default {
135
134
  data: () => ({
136
135
  filtersOpen: false,
137
136
  }),
137
+ computed: {
138
+ isDesktopMq() {
139
+ return matchesMqQuery(this.mq, 'lg+')
140
+ },
141
+ isMobileMq() {
142
+ return matchesMqQuery(this.mq, ['sm', 'md'])
143
+ },
144
+ },
138
145
 
139
146
  methods: {
140
147
  clearFilters() {
@@ -10,7 +10,6 @@
10
10
 
11
11
  <script>
12
12
  import { createFocusTrap } from 'focus-trap'
13
- import createLegacyDestroyHooks from '../../utils/createLegacyDestroyHooks'
14
13
 
15
14
  export default {
16
15
  name: 'FocusTrap',
@@ -53,9 +52,9 @@ export default {
53
52
  this.syncTrapState()
54
53
  })
55
54
  },
56
- ...createLegacyDestroyHooks(function() {
55
+ beforeUnmount() {
57
56
  this.disposeTrap()
58
- }),
57
+ },
59
58
  methods: {
60
59
  syncTrapState() {
61
60
  if (!this.trap) return
@@ -8,7 +8,6 @@
8
8
  </template>
9
9
 
10
10
  <script>
11
- import createLegacyDestroyHooks from '../../utils/createLegacyDestroyHooks'
12
11
 
13
12
  export default {
14
13
  name: 'Intersect',
@@ -29,9 +28,9 @@ export default {
29
28
  updated() {
30
29
  this.refreshObserver()
31
30
  },
32
- ...createLegacyDestroyHooks(function() {
31
+ beforeUnmount() {
33
32
  this.resetObserver()
34
- }),
33
+ },
35
34
  methods: {
36
35
  getTarget() {
37
36
  const root = this.$refs.root
@@ -9,7 +9,6 @@
9
9
 
10
10
  <script>
11
11
  import { DEFAULT_MQ, getResponsiveMq, matchesMqQuery } from '../../utils/responsive-mq'
12
- import createLegacyDestroyHooks from '../../utils/createLegacyDestroyHooks'
13
12
 
14
13
  export default {
15
14
  name: 'MqLayout',
@@ -43,10 +42,10 @@ export default {
43
42
  if (typeof window === 'undefined') return
44
43
  window.addEventListener('resize', this.updateMq)
45
44
  },
46
- ...createLegacyDestroyHooks(function() {
45
+ beforeUnmount() {
47
46
  if (typeof window === 'undefined') return
48
47
  window.removeEventListener('resize', this.updateMq)
49
- }),
48
+ },
50
49
 
51
50
  methods: {
52
51
  updateMq() {
@@ -128,7 +128,6 @@
128
128
  </template>
129
129
 
130
130
  <script>
131
- import createLegacyDestroyHooks from '../../utils/createLegacyDestroyHooks'
132
131
 
133
132
  function normalizeOption(option, optionLabel, trackBy) {
134
133
  if (option && typeof option === 'object') return option
@@ -263,9 +262,9 @@ export default {
263
262
  document.addEventListener('mousedown', this.handleDocumentMouseDown)
264
263
  }
265
264
  },
266
- ...createLegacyDestroyHooks(function() {
265
+ beforeUnmount() {
267
266
  this.removeDocumentListener()
268
- }),
267
+ },
269
268
  methods: {
270
269
  getOptionKey(option) {
271
270
  const normalized = normalizeOption(option, this.label, this.trackBy)
@@ -12,7 +12,6 @@
12
12
  </template>
13
13
 
14
14
  <script>
15
- import createLegacyDestroyHooks from '../../utils/createLegacyDestroyHooks'
16
15
 
17
16
  const RECAPTCHA_SCRIPT_ID = 'asd20-recaptcha-script'
18
17
  const RECAPTCHA_SCRIPT_SRC =
@@ -92,9 +91,9 @@ export default {
92
91
  mounted() {
93
92
  if (this.loadRecaptchaScript) this.ensureWidget()
94
93
  },
95
- ...createLegacyDestroyHooks(function() {
94
+ beforeUnmount() {
96
95
  this.reset()
97
- }),
96
+ },
98
97
  methods: {
99
98
  async getRecaptcha() {
100
99
  if (!isBrowser()) return null
@@ -48,14 +48,16 @@ function mountLazyImage(el, binding = {}) {
48
48
  }
49
49
 
50
50
  el.setAttribute('src', TRANSPARENT_PIXEL)
51
- el.__lazyImageObserver = new window.IntersectionObserver(
51
+ const observer = new window.IntersectionObserver(
52
52
  entries => {
53
+ if (el.__lazyImageObserver !== observer) return
53
54
  if (!entries.some(entry => entry.isIntersecting)) return
54
55
  loadLazyImage(el)
55
56
  },
56
57
  { rootMargin: '200px 0px' }
57
58
  )
58
- el.__lazyImageObserver.observe(el)
59
+ el.__lazyImageObserver = observer
60
+ observer.observe(el)
59
61
  }
60
62
 
61
63
  export default {
@@ -331,7 +331,7 @@ export default function mapPageQueryResultToPageTemplateProps(queryResult) {
331
331
  // In the comm-center project look at
332
332
  // /packages/configurations/helpers/*
333
333
 
334
- // TODO: Eventually move this function into a seperate package
334
+ // TODO: Eventually move this function into a separate package
335
335
  // Make this package a part of the comm-center project or ui project
336
336
  // e.g. /packages/template-utilities
337
- // Possible you could combine the configurations packages, or create a seperate package
337
+ // Possible you could combine the configurations packages, or create a separate package
@@ -15,8 +15,7 @@ function omitKeys(source, keys) {
15
15
  export default {
16
16
  inheritAttrs: false,
17
17
  props: {
18
- value: { default: '' },
19
- modelValue: { default: undefined },
18
+ modelValue: { default: '' },
20
19
  label: { type: String, default: '' },
21
20
  hideLabel: { type: Boolean, default: false },
22
21
  helpText: { type: String, default: '' },
@@ -89,7 +88,7 @@ export default {
89
88
  return this.validationErrors.length === 0
90
89
  },
91
90
  resolvedValue() {
92
- return this.modelValue !== undefined ? this.modelValue : this.value
91
+ return this.modelValue
93
92
  },
94
93
 
95
94
  computedId() {
@@ -133,9 +132,6 @@ export default {
133
132
  inputAttrs() {
134
133
  return omitKeys(this.$attrs, INPUT_MODEL_LISTENER_KEYS)
135
134
  },
136
- legacyListeners() {
137
- return omitKeys(this.$options._parentListeners || {}, ['input'])
138
- },
139
135
  },
140
136
  watch: {
141
137
  resolvedValue(value) {
@@ -192,7 +188,6 @@ export default {
192
188
  input(event) {
193
189
  this.isDirty = true
194
190
  this.validate(event.target.value)
195
- this.$emit('input', event.target.value)
196
191
  this.$emit('update:modelValue', event.target.value)
197
192
  },
198
193
  },
@@ -1,5 +1,4 @@
1
1
  import { DEFAULT_MQ, getResponsiveMq } from '../utils/responsive-mq'
2
- import createLegacyDestroyHooks from '../utils/createLegacyDestroyHooks'
3
2
 
4
3
  export default {
5
4
  data: () => ({
@@ -20,10 +19,10 @@ export default {
20
19
  if (typeof window === 'undefined') return
21
20
  window.addEventListener('resize', this.updateResponsiveMq)
22
21
  },
23
- ...createLegacyDestroyHooks(function() {
22
+ beforeUnmount() {
24
23
  if (typeof window === 'undefined') return
25
24
  window.removeEventListener('resize', this.updateResponsiveMq)
26
- }),
25
+ },
27
26
 
28
27
  methods: {
29
28
  updateResponsiveMq() {
@@ -1,10 +0,0 @@
1
- export default function createLegacyDestroyHooks(cleanup) {
2
- return {
3
- beforeDestroy() {
4
- cleanup.call(this)
5
- },
6
- beforeUnmount() {
7
- cleanup.call(this)
8
- },
9
- }
10
- }