@asd20/ui-next 1.0.10 → 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 (51) hide show
  1. package/CHANGELOG.md +12 -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/Asd20ActionMenu/index.vue +0 -12
  21. package/src/components/organisms/Asd20AiSearch/index.vue +20 -3
  22. package/src/components/organisms/Asd20PageFooter/index.vue +0 -22
  23. package/src/components/organisms/Asd20SchoolHomepageVideoHeader/index.vue +2 -3
  24. package/src/components/organisms/Asd20SecondaryHeader/index.vue +2 -3
  25. package/src/components/organisms/Asd20SiteNavigation/index.vue +2 -3
  26. package/src/components/organisms/Asd20SiteSearch/index.vue +124 -80
  27. package/src/components/organisms/Asd20SwiperFeed/index.vue +1 -1
  28. package/src/components/organisms/Asd20TabBar/index.vue +2 -3
  29. package/src/components/organisms/Asd20VideoHeader/index.vue +2 -3
  30. package/src/components/templates/Asd20AppTemplate/index.vue +17 -10
  31. package/src/components/templates/Asd20ArticleDigestCompactTemplate/index.vue +4 -4
  32. package/src/components/templates/Asd20ArticleDigestTemplate/index.vue +5 -5
  33. package/src/components/templates/Asd20ArticleListTemplate/index.vue +4 -4
  34. package/src/components/templates/Asd20ClubsTemplate/index.vue +4 -4
  35. package/src/components/templates/Asd20DistrictVideoTemplate/index.vue +20 -0
  36. package/src/components/templates/Asd20SalaryCalculatorTemplate/index.vue +0 -2
  37. package/src/components/templates/Asd20SchoolHomeTemplate/index.vue +20 -0
  38. package/src/components/templates/Asd20SchoolHomeVideoTemplate/index.vue +20 -0
  39. package/src/components/templates/Asd20SearchAppTemplate/index.vue +17 -10
  40. package/src/components/utils/FocusTrap.vue +2 -3
  41. package/src/components/utils/Intersect.vue +2 -3
  42. package/src/components/utils/MqLayout.vue +2 -3
  43. package/src/components/utils/Multiselect.vue +2 -3
  44. package/src/components/utils/Recaptcha.vue +2 -3
  45. package/src/directives/lazy-image.js +4 -2
  46. package/src/helpers/mapPageQueryResultToPageTemplateProps.js +2 -2
  47. package/src/mixins/globalPropMixinFactory.js +46 -3
  48. package/src/mixins/hasLoaderMixin.js +14 -0
  49. package/src/mixins/inputComponentMixin.js +2 -7
  50. package/src/mixins/responsiveBreakpointMixin.js +2 -3
  51. package/src/utils/createLegacyDestroyHooks.js +0 -10
@@ -96,8 +96,18 @@
96
96
  <client-only>
97
97
  <Asd20AiSearch
98
98
  class="ai-search"
99
+ :pages="searchPages"
100
+ :files="searchFiles"
101
+ :groups="searchGroups"
102
+ :include-district-results="includeDistrictResults"
103
+ :search-language-code="searchLanguageCode"
104
+ :query-pages-handler="queryPagesHandler"
105
+ :query-files-handler="queryFilesHandler"
106
+ :query-groups-handler="queryGroupsHandler"
107
+ :query-ai-site-handler="queryAiSiteHandler"
99
108
  :organization="organization"
100
109
  :organization-options="organizationOptions"
110
+ :ai-search-feedback-form-url="aiSearchFeedbackFormUrl"
101
111
  />
102
112
  </client-only>
103
113
 
@@ -221,6 +231,16 @@ export default {
221
231
  mixins: [pageTemplateMixin],
222
232
  props: {
223
233
  languageCode: { type: String, default: 'en' },
234
+ searchPages: { type: Array, default: () => [] },
235
+ searchFiles: { type: Array, default: () => [] },
236
+ searchGroups: { type: Array, default: () => [] },
237
+ includeDistrictResults: { type: Boolean, default: true },
238
+ searchLanguageCode: { type: String, default: null },
239
+ queryPagesHandler: { type: Function, default: null },
240
+ queryFilesHandler: { type: Function, default: null },
241
+ queryGroupsHandler: { type: Function, default: null },
242
+ queryAiSiteHandler: { type: Function, default: null },
243
+ aiSearchFeedbackFormUrl: { type: String, default: '' },
224
244
  },
225
245
  computed: {
226
246
  blockEvent() {
@@ -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
@@ -1,37 +1,80 @@
1
1
  // pass a variable, the prop options as it would be defined in vue, and optionally the store module, which will become a module named for convenience in the store.
2
2
 
3
+ function hyphenate(value) {
4
+ return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
5
+ }
6
+
7
+ function resolvePropDefault(propOptions = {}) {
8
+ const defaultValue = propOptions.default
9
+ if (typeof defaultValue === 'function' && propOptions.type !== Function) {
10
+ return defaultValue()
11
+ }
12
+ return defaultValue
13
+ }
14
+
15
+ function hasIncomingProp(vm, propName) {
16
+ const rawProps = vm && vm.$ && vm.$.vnode && vm.$.vnode.props
17
+ if (!rawProps) return false
18
+
19
+ return (
20
+ Object.prototype.hasOwnProperty.call(rawProps, propName) ||
21
+ Object.prototype.hasOwnProperty.call(rawProps, hyphenate(propName))
22
+ )
23
+ }
24
+
3
25
  export default function(variableName, propOptions, storeModuleName) {
4
26
  const pascalCaseName = variableName[0].toUpperCase() + variableName.slice(1)
5
27
  const resolvedName = `resolved${pascalCaseName}`
28
+ const localStateName = `local${pascalCaseName}`
6
29
  let mixin = { props: {}, computed: {}, methods: {} }
7
30
  mixin.props[variableName] = propOptions
31
+ mixin.data = function() {
32
+ return {
33
+ [localStateName]: resolvePropDefault(propOptions),
34
+ }
35
+ }
8
36
  // Returns a computed alias that resolves from the store when available,
9
37
  // otherwise falling back to the local prop value.
10
38
  mixin.computed[resolvedName] = {
11
39
  get() {
40
+ if (hasIncomingProp(this, variableName)) {
41
+ return this[variableName]
42
+ }
43
+
12
44
  try {
13
- return storeModuleName
45
+ const storeValue = storeModuleName
14
46
  ? this.$store['state'][storeModuleName][variableName]
15
47
  : this.$store.state[variableName]
48
+ return storeValue !== undefined ? storeValue : this[localStateName]
16
49
  } catch {
17
- return this[variableName]
50
+ return this[localStateName]
18
51
  }
19
52
  },
20
53
  // emit an update event and value and call method to set value in store.
21
54
  set(value) {
22
55
  this.$emit(`update:${variableName}`, value)
56
+
57
+ if (hasIncomingProp(this, variableName)) {
58
+ return
59
+ }
60
+
23
61
  try {
24
- this[`set${pascalCaseName}`](value)
62
+ if (this[`set${pascalCaseName}`](value)) return
25
63
  } catch {
26
64
  // throw away
27
65
  }
66
+
67
+ this[localStateName] = value
28
68
  },
29
69
  }
30
70
  mixin.methods[`set${pascalCaseName}`] = function(value) {
71
+ if (!this.$store || typeof this.$store.dispatch !== 'function') return false
72
+
31
73
  this.$store.dispatch(
32
74
  `${storeModuleName ? storeModuleName + '/' : ''}set${pascalCaseName}`,
33
75
  value
34
76
  )
77
+ return true
35
78
  }
36
79
  return mixin
37
80
  }
@@ -1,7 +1,21 @@
1
+ function hasIncomingProp(vm, propName) {
2
+ const rawProps = vm && vm.$ && vm.$.vnode && vm.$.vnode.props
3
+ if (!rawProps) return false
4
+
5
+ return (
6
+ Object.prototype.hasOwnProperty.call(rawProps, propName) ||
7
+ Object.prototype.hasOwnProperty.call(rawProps, 'is-loading')
8
+ )
9
+ }
10
+
1
11
  export default function(variableName, storeModuleName) {
2
12
  let mixin = { props: {}, computed: {} }
3
13
  mixin.props.isLoading = { type: Boolean, default: false }
4
14
  mixin.computed[`_${variableName}`] = function() {
15
+ if (hasIncomingProp(this, 'isLoading')) {
16
+ return this.isLoading
17
+ }
18
+
5
19
  if (
6
20
  storeModuleName &&
7
21
  this.$store &&
@@ -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
- }