@worksafevictoria/wcl7.5 1.16.0 → 1.17.0-beta.10

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 (45) hide show
  1. package/package.json +2 -1
  2. package/src/assets/icons/chevron-right-icon.svg +14 -0
  3. package/src/assets/icons/notepad.svg +93 -0
  4. package/src/assets/icons/question.svg +7 -0
  5. package/src/components/Common/CardGrid/index.vue +83 -74
  6. package/src/components/Common/CardGridItem/index.vue +31 -4
  7. package/src/components/Containers/Carousel/index.vue +11 -2
  8. package/src/components/Containers/HomepageHeaderNew/index.vue +4 -4
  9. package/src/components/Containers/SectionGroup/index.stories.js +28 -27
  10. package/src/components/Containers/SectionGroup/index.vue +6 -0
  11. package/src/components/Global/AppHeaderNew/index.vue +10 -2
  12. package/src/components/Global/AppHeaderNew/mobile.scss +5 -2
  13. package/src/components/Global/AppHeaderNew/styles.scss +6 -5
  14. package/src/components/Global/HeroHeader/index.vue +74 -57
  15. package/src/components/Paragraphs/Accordion/index.stories.js +3 -3
  16. package/src/components/Paragraphs/Accordion/index.vue +3 -4
  17. package/src/components/Paragraphs/Calculator/CardContainer/index.vue +68 -60
  18. package/src/components/Paragraphs/Calculator/index.vue +24 -22
  19. package/src/components/Paragraphs/Directory/Asbestos/Records/SingleRecord/index.vue +1 -16
  20. package/src/components/Paragraphs/Directory/Asbestos/Records/index.vue +0 -39
  21. package/src/components/Paragraphs/RTWPlanner/CardContainer/index.vue +133 -0
  22. package/src/components/Paragraphs/RTWPlanner/Constants.js +433 -0
  23. package/src/components/Paragraphs/RTWPlanner/Footer/index.vue +32 -0
  24. package/src/components/Paragraphs/RTWPlanner/{NavBar → Header}/index.vue +20 -9
  25. package/src/components/Paragraphs/RTWPlanner/HomePage/index.vue +72 -0
  26. package/src/components/Paragraphs/RTWPlanner/Injuries/index.vue +220 -0
  27. package/src/components/Paragraphs/RTWPlanner/index.stories.js +79 -15
  28. package/src/components/Paragraphs/RTWPlanner/index.vue +99 -31
  29. package/src/components/Paragraphs/SelectableCards/Control/index.stories.js +31 -11
  30. package/src/components/Paragraphs/SelectableCards/cardbody.vue +9 -10
  31. package/src/components/Paragraphs/SelectableCards/cardtop.vue +23 -16
  32. package/src/components/Paragraphs/SelectableCards/index.stories.js +29 -8
  33. package/src/components/Paragraphs/SelectableCards/index.vue +124 -50
  34. package/src/components/Paragraphs/TextMedia/index.vue +4 -0
  35. package/src/components/Paragraphs/VideoPlayer/index.vue +1 -1
  36. package/src/components/SubComponents/Breadcrumb/index.vue +40 -7
  37. package/src/components/SubComponents/FormInstance/services/logic-parser.js +44 -12
  38. package/src/components/SubComponents/MediaPlayer/index.vue +9 -11
  39. package/src/components/SubComponents/ResourceGroup/index.vue +1 -1
  40. package/src/includes/scss/vars/src/colors.module.scss +3 -0
  41. package/src/includes/scss/vars/src/colors.scss +3 -0
  42. package/src/index.js +2 -2
  43. package/src/mock/asbestos-removalists.js +9 -136
  44. package/src/mock/control-selectable-cards.js +68 -25
  45. package/src/components/Paragraphs/RTWPlanner/Home/index.vue +0 -83
@@ -7,55 +7,99 @@
7
7
  :sub-heading="subHeading"
8
8
  :columns-per-row="columns || 4"
9
9
  class="selectable-cards"
10
- :is-selectable="true"
10
+ :is-selectable="isSelectable"
11
11
  :card-id-prefix="'selectable-cc-card'"
12
12
  @selected="selectedCardChangeFocus"
13
+ @selected-button-role="selectedButtonRole"
13
14
  >
14
- <template v-slot:cardItem="{ card }">
15
- <!-----CONTROL SELECTED CARD START-------->
15
+ <template v-slot:cardItem="slotProps">
16
16
  <card-grid-item
17
17
  v-if="cardType === 'control-selectcards'"
18
18
  class="control_selectcard"
19
- :button-role="'radio'"
20
- :glyph-src="card.icon"
19
+ :button-role="buttonRole"
20
+ :glyph-src="normalize(slotProps).icon"
21
21
  :icon-size="'medium'"
22
22
  :show-divider="false"
23
23
  :header-size="'medium'"
24
- :card-header-title="card.name"
25
- :card-padding="'small'"
24
+ :card-header-title="normalize(slotProps).name"
25
+ :card-padding="cardPadding"
26
26
  :border-type="'dark'"
27
27
  :border-on-select-background="'blue'"
28
28
  :icon-is-bordered="iconIsBordered"
29
29
  :card-text-align="cardTextAlign"
30
+ :is-selectable="isSelectable"
30
31
  >
31
- <template v-slot:cardTop>
32
- <card-top :card="card" :button-role="'radio'" :is-selectable="true" />
32
+ <template #cardTop>
33
+ <card-top
34
+ v-if="buttonRole !== 'none'"
35
+ :card="normalize(slotProps)"
36
+ :button-role="buttonRole"
37
+ :is-selectable="true"
38
+ @button-role-clicked="selectedButtonRole"
39
+ />
33
40
  </template>
34
- <template v-slot:cardDescription>
35
- <card-body :card="card" />
41
+
42
+ <template #cardDescription>
43
+ <card-body :card="normalize(slotProps)" />
36
44
  </template>
37
45
  </card-grid-item>
38
- <!-----CONTROL SELECTED CARD END-------->
46
+
39
47
  <card-grid-item
40
48
  v-else
41
49
  class="selectcard"
42
- :glyph-src="card.icon"
50
+ :glyph-src="normalize(slotProps).icon"
43
51
  :show-divider="false"
44
52
  :header-size="'large'"
45
- :card-header-title="card.name"
53
+ :card-header-title="normalize(slotProps).name"
46
54
  :card-padding="'small'"
47
55
  :invert-on-select-background="'black'"
48
56
  :border-type="'shadow'"
49
- :description="card.description"
57
+ :description="normalize(slotProps).description"
50
58
  />
51
59
  </template>
52
- <template v-slot:cardGridFooter>
53
- <slot name="cardGridFooter"></slot>
60
+
61
+ <template #cardGridFooter>
62
+ <slot name="cardGridFooter" />
54
63
  </template>
55
64
  </card-grid>
65
+
66
+ <!-- Reusable modal lives here now -->
67
+ <BModal
68
+ v-if="showModalOnButtonRole"
69
+ v-model="showModal"
70
+ :size="modalSize"
71
+ :no-footer="noModalFooter"
72
+ :scrollable="true"
73
+ :class="modalClass"
74
+ @show="$emit('modal-open', modalCard)"
75
+ @hide="$emit('modal-close')"
76
+ >
77
+ <div v-if="modalCard" class="selectable-cards__modal">
78
+ <!-- Parent can fully control; -->
79
+ <slot name="modalTitle" :card="modalCard">
80
+ <h2 class="card-title">{{ modalCard.name }}</h2>
81
+ </slot>
82
+
83
+ <slot name="modalBody" :card="modalCard">
84
+ <div class="card-body">
85
+ <p v-if="modalCard.value" class="card-value">{{ modalCard.value }}</p>
86
+ <div
87
+ v-if="modalCard.description"
88
+ class="card-description"
89
+ v-html="modalCard.description"
90
+ />
91
+ </div>
92
+ </slot>
93
+
94
+ <slot name="modalFooter" :card="modalCard">
95
+ <div />
96
+ </slot>
97
+ </div>
98
+ </BModal>
56
99
  </template>
57
100
 
58
101
  <script>
102
+ import { BModal } from 'bootstrap-vue-next'
59
103
  import CardGrid from './../../Common/CardGrid/index.vue'
60
104
  import CardGridItem from './../../Common/CardGridItem/index.vue'
61
105
  import CardBody from './cardbody.vue'
@@ -63,56 +107,86 @@ import CardTop from './cardtop.vue'
63
107
 
64
108
  export default {
65
109
  name: 'SelectableCards',
66
- components: {
67
- CardGrid,
68
- CardGridItem,
69
- CardBody,
70
- CardTop,
71
- },
110
+ components: { CardGrid, CardGridItem, CardBody, CardTop, BModal },
72
111
  emits: {
73
112
  selected: false,
113
+ 'selected-button-role': false,
114
+ 'modal-open': false,
115
+ 'modal-close': false,
74
116
  },
75
117
  props: {
76
- cards: {
77
- type: Array,
78
- required: true,
79
- default: () => [],
80
- },
81
- title: {
82
- type: String,
83
- default: null,
84
- },
85
- headingTag: {
86
- type: String,
87
- default: 'h2',
88
- },
89
- subHeading: {
118
+ cards: { type: Array, required: true, default: () => [] },
119
+ title: { type: String, default: null },
120
+ headingTag: { type: String, default: 'h2' },
121
+ subHeading: { type: String, default: null },
122
+ iconIsBordered: { type: Boolean, default: true },
123
+ isSelectable: { type: Boolean, default: true },
124
+ columns: { type: Number, default: 4 },
125
+ cardType: { type: String, default: null },
126
+ cardTextAlign: { type: String, default: null },
127
+ buttonRole: { type: String, default: 'radio' },
128
+ cardPadding: {
90
129
  type: String,
91
- default: null,
130
+ default: 'small',
131
+ validator: (value) =>
132
+ ['none', 'xsmall', 'small', 'medium', 'large'].indexOf(value) >= 0,
92
133
  },
93
- iconIsBordered: {
134
+
135
+ /* New modal controls for reusability */
136
+ showModalOnButtonRole: {
94
137
  type: Boolean,
95
138
  default: true,
96
139
  },
97
- columns: {
98
- type: Number,
99
- default: 4,
100
- },
101
- cardType: {
140
+ modalSize: {
102
141
  type: String,
103
- default: null,
142
+ default: 'xl',
104
143
  },
105
- cardTextAlign: {
144
+ modalClass: {
106
145
  type: String,
107
- default: null,
146
+ default: 'wcl-card-modal__modal',
108
147
  },
148
+ noModalFooter: {
149
+ type: Boolean,
150
+ default: true,
151
+ },
152
+ },
153
+ data() {
154
+ return {
155
+ showModal: false,
156
+ modalCard: null,
157
+ }
109
158
  },
110
159
  methods: {
111
- selectedCardChangeFocus(card) {
112
- this.$emit('selected', card.selectedCard)
160
+ // Accept various slot shapes and return the raw card
161
+ normalize(slotProps) {
162
+ const c =
163
+ slotProps?.card?.selectedCard ??
164
+ slotProps?.card ??
165
+ slotProps?.item ??
166
+ slotProps?.selectedCard ??
167
+ slotProps
168
+ return c || {}
113
169
  },
170
+
171
+ selectedCardChangeFocus(payload) {
172
+ // If CardGrid emits { selectedCard }, keep behavior consistent
173
+ const card = payload?.selectedCard ?? payload
174
+ this.$emit('selected', card)
175
+ },
176
+
177
+ selectedButtonRole(card) {
178
+ // Receive raw card from <card-top> and bubble it up as raw object
179
+ this.$emit('selected-button-role', card)
180
+
181
+ // open the reusable modal here
182
+ if (this.showModalOnButtonRole) {
183
+ this.modalCard = card
184
+ this.showModal = true
185
+ }
186
+ },
187
+
114
188
  clearCard() {
115
- this.$refs.cardgrid.clearCards()
189
+ this.$refs.cardgrid?.clearCards?.()
116
190
  },
117
191
  },
118
192
  }
@@ -177,6 +177,10 @@ export default {
177
177
  }
178
178
  }
179
179
 
180
+ &__title {
181
+ margin-bottom: 24px;
182
+ }
183
+
180
184
  /* variants */
181
185
  &.rtl {
182
186
  text-align: left;
@@ -164,7 +164,7 @@ export default {
164
164
  {
165
165
  field_title: this.transcriptTitle,
166
166
  field_rich_text: {
167
- value: this.transcript,
167
+ processed: this.transcript,
168
168
  },
169
169
  },
170
170
  ]
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  class="wcl-breadcrumb"
4
- :class="{ ['wcl-breadcrumb--rtl']: rtl, ['wcl-breadcrumb--ltr']: !rtl }"
4
+ :class="{ 'wcl-breadcrumb--rtl': rtl, 'wcl-breadcrumb--ltr': !rtl }"
5
5
  >
6
6
  <ol class="breadcrumb">
7
7
  <li
@@ -9,27 +9,60 @@
9
9
  :key="index"
10
10
  class="breadcrumb-item"
11
11
  >
12
- <a v-if="item.href" :href="item.href" target="_self">{{ item.text }}</a>
13
- <nuxt-link v-else-if="item.to" :to="item.to">{{ item.text }}</nuxt-link>
12
+ <!-- Plain anchor -->
13
+ <a
14
+ v-if="item.href"
15
+ :href="item.href"
16
+ target="_self"
17
+ @click="onClick(item, index, $event)"
18
+ >{{ item.text }}</a
19
+ >
20
+
21
+ <!-- NuxtLink (Nuxt 3 / Vue 3) -->
22
+ <nuxt-link
23
+ v-else-if="item.to"
24
+ :to="item.to"
25
+ @click="onClick(item, index, $event)"
26
+ >{{ item.text }}</nuxt-link
27
+ >
28
+
29
+ <!-- Fallback (no link) -->
30
+ <span v-else @click="onClick(item, index, $event)">{{
31
+ item.text
32
+ }}</span>
14
33
  </li>
15
34
  </ol>
16
35
  </div>
17
36
  </template>
37
+
18
38
  <script>
19
39
  export default {
20
40
  name: 'Breadcrumb',
21
41
  props: {
22
42
  breadcrumbItems: {
23
43
  type: Array,
24
- required: true
44
+ required: true,
25
45
  },
26
46
  rtl: {
27
47
  type: Boolean,
28
- required: false
29
- }
30
- }
48
+ required: false,
49
+ },
50
+ // If true, prevent default navigation and let the parent handle it.
51
+ interceptClicks: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+ },
56
+ emits: ['item-click'],
57
+ methods: {
58
+ onClick(item, index, event) {
59
+ this.$emit('item-click', { item, index, event })
60
+ if (this.interceptClicks) event.preventDefault()
61
+ },
62
+ },
31
63
  }
32
64
  </script>
65
+
33
66
  <style lang="scss" scoped>
34
67
  @import '../../../includes/scss/vars/src/colors.scss';
35
68
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  convertStringWithDotNotationToObject,
3
3
  generateUniqueId,
4
- isEmpty
4
+ isEmpty,
5
5
  } from '../models/form-utils'
6
6
 
7
7
  export function parseElementStates(webformElement) {
@@ -75,17 +75,38 @@ function ruleToString(rule) {
75
75
  }
76
76
 
77
77
  function ruleItemToString(ruleItem, itemKey) {
78
- const fieldName = itemKey.split(' ')[1] || itemKey.split(' ')[0]
79
- const formElementID = fieldName?.match(/"([^']+)"/)[1]
78
+ // Regex 1: Matches standard selectors (name="foo"). Captures 'foo'.
79
+ const standardMatch = itemKey.match(/name="([^"]+)"/)
80
+ // Regex 2: Matches array selectors (name="foo[bar]"). Captures 'foo' and 'bar'.
81
+ const arrayMatch = itemKey.match(/name="([^"]+)\[([^\]]+)\]"/)
82
+
83
+ let formComponentID = null // The Formio component key (e.g., 'checkboxes')
84
+ let checkboxOptionKey = null // The specific option key (e.g., 'foo bar')
85
+
86
+ if (arrayMatch && arrayMatch[1] && arrayMatch[2]) {
87
+ // Array Notation found (e.g., name="checkboxes[foo bar]")
88
+ formComponentID = arrayMatch[1]
89
+ checkboxOptionKey = arrayMatch[2]
90
+ } else if (standardMatch && standardMatch[1]) {
91
+ // Standard Notation found (e.g., name="foobar")
92
+ formComponentID = standardMatch[1]
93
+ }
94
+
95
+ // ... (rest of the function)
96
+
97
+ // Now pass BOTH keys to getTrigger for array-based lookups
80
98
  const conditions = Object.keys(ruleItem)
81
99
  .map((elementProp) =>
82
- formElementID ? getTrigger(formElementID, ruleItem, elementProp) : null
100
+ formComponentID
101
+ ? getTrigger(formComponentID, ruleItem, elementProp, checkboxOptionKey)
102
+ : null,
83
103
  )
84
104
  .filter((trigger) => !!trigger)
85
105
  .reduce((triggers, trigger) => {
86
106
  triggers.push(trigger)
87
107
  return triggers
88
108
  }, [])
109
+
89
110
  return conditions.join('')
90
111
  }
91
112
 
@@ -95,7 +116,7 @@ function createNewLogic(actionName, actionValue) {
95
116
  convertStringWithDotNotationToObject(
96
117
  defaultProps,
97
118
  actionName,
98
- !!!actionValue
119
+ !!!actionValue,
99
120
  )
100
121
  } else {
101
122
  defaultProps[actionName] = !!!actionValue
@@ -104,7 +125,7 @@ function createNewLogic(actionName, actionValue) {
104
125
  name: generateUniqueId(),
105
126
  trigger: {
106
127
  type: 'javascript',
107
- javascript: ''
128
+ javascript: '',
108
129
  },
109
130
  defaultProps,
110
131
  actions: [
@@ -114,20 +135,31 @@ function createNewLogic(actionName, actionValue) {
114
135
  property: {
115
136
  label: generateUniqueId(),
116
137
  value: actionName,
117
- type: 'boolean'
138
+ type: 'boolean',
118
139
  },
119
- state: actionValue
120
- }
121
- ]
140
+ state: actionValue,
141
+ },
142
+ ],
122
143
  }
123
144
  }
124
145
 
125
- function getTrigger(formElementID, ruleItem, prop) {
146
+ function getTrigger(formComponentID, ruleItem, prop, checkboxOptionKey = null) {
126
147
  if (ruleItem.hasOwnProperty(prop)) {
127
148
  const value = ruleItem[prop] ?? ''
149
+
150
+ let variable = `data.{${formComponentID}}`
151
+ console.log('🚀 ~ getTrigger ~ variable:', variable)
128
152
  let valueDelimeter = typeof value === 'string' ? "'" : ''
129
153
  let expression = `${valueDelimeter}${value}${valueDelimeter}`
130
- let variable = `data.{${formElementID}}`
154
+
155
+ if (checkboxOptionKey && prop === 'checked' && value === true) {
156
+ return `(${variable}['${checkboxOptionKey}']===true)`
157
+ }
158
+
159
+ if (checkboxOptionKey && prop === 'unchecked' && value === true) {
160
+ return `(!(${variable}['${checkboxOptionKey}']===true))`
161
+ }
162
+
131
163
  if (typeof value !== 'object' && prop === 'value') {
132
164
  return `${variable} === ${expression}`
133
165
  } else if (typeof value === 'object' && prop === 'value') {
@@ -6,9 +6,8 @@
6
6
  <iframe
7
7
  v-if="provider === 'youtube'"
8
8
  class="videoFrame"
9
- :src="
10
- `https://www.youtube-nocookie.com/embed/${videoId}?iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1`
11
- "
9
+ :src="`https://www.youtube-nocookie.com/embed/${videoId}?iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1`"
10
+ referrerpolicy="strict-origin-when-cross-origin"
12
11
  allowfullscreen
13
12
  allowtransparency
14
13
  allow="autoplay"
@@ -16,9 +15,8 @@
16
15
  <iframe
17
16
  v-if="provider === 'vimeo'"
18
17
  class="videoFrame"
19
- :src="
20
- `https://player.vimeo.com/video/${videoId}?color=02819e&autopause=0&byline=0&dnt=1&fun=0&playsinline=0&portrait=0&title=0&transparent=0`
21
- "
18
+ :src="`https://player.vimeo.com/video/${videoId}?color=02819e&autopause=0&byline=0&dnt=1&fun=0&playsinline=0&portrait=0&title=0&transparent=0`"
19
+ referrerpolicy="strict-origin-when-cross-origin"
22
20
  allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen;"
23
21
  allowtransparency
24
22
  ></iframe>
@@ -31,17 +29,17 @@ export default {
31
29
  props: {
32
30
  videoId: {
33
31
  type: String,
34
- required: true
32
+ required: true,
35
33
  },
36
34
  provider: {
37
35
  type: String,
38
- required: true
36
+ required: true,
39
37
  },
40
38
  rtl: {
41
39
  type: Boolean,
42
- default: false
43
- }
44
- }
40
+ default: false,
41
+ },
42
+ },
45
43
  }
46
44
  </script>
47
45
 
@@ -177,7 +177,7 @@ export default {
177
177
  },
178
178
  initialDisplyLimit: {
179
179
  type: Number,
180
- default: 0,
180
+ default: 1000,
181
181
  },
182
182
  fetchContent: {
183
183
  type: Function,
@@ -20,6 +20,8 @@ $yellow: #ffd229;
20
20
  $lightyellow: #fff6d4;
21
21
  $outline: #da47ff;
22
22
  $outline-dark: #ffffff;
23
+ $lavendergray: #e5e8f2;
24
+
23
25
  // Functional colours
24
26
  $wsv-fun-dark-green: #576D2F;
25
27
  $wsv-fun-dark-blue: #104f77;
@@ -58,6 +60,7 @@ $theme-colors: (
58
60
  'lightyellow': $lightyellow,
59
61
  'outline': $outline,
60
62
  'outline-dark': $outline-dark,
63
+ 'lavendergray': $lavendergray,
61
64
  // Functional colours
62
65
  'wsv-fun-dark-green':$wsv-fun-dark-green,
63
66
  'wsv-fun-dark-blue':$wsv-fun-dark-blue,
@@ -20,6 +20,8 @@ $yellow: #ffd229;
20
20
  $lightyellow: #fff6d4;
21
21
  $outline: #da47ff;
22
22
  $outline-dark: #ffffff;
23
+ $lavendergray: #e5e8f2;
24
+
23
25
  // Functional colours
24
26
  $wsv-fun-dark-green: #576D2F;
25
27
  $wsv-fun-dark-blue: #104f77;
@@ -59,6 +61,7 @@ $theme-colors: (
59
61
  'lightyellow': $lightyellow,
60
62
  'outline': $outline,
61
63
  'outline-dark': $outline-dark,
64
+ 'lavendergray': $lavendergray,
62
65
  // Functional colours
63
66
  'wsv-fun-dark-green':$wsv-fun-dark-green,
64
67
  'wsv-fun-dark-blue':$wsv-fun-dark-blue,
package/src/index.js CHANGED
@@ -2,12 +2,12 @@
2
2
  import Container from './components/Containers/Container/index.vue'
3
3
  import Row from './components/Containers/Row/index.vue'
4
4
  import Column from './components/Containers/Column/index.vue'
5
- import HomepageHeader from './components/Containers/HomepageHeader/index.vue'
5
+ import HomepageHeader from './components/Containers/HomepageHeaderNew/index.vue'
6
6
  import Subheader from './components/Containers/Subheader/index.vue'
7
7
  import SectionGroup from './components/Containers/SectionGroup/index.vue'
8
8
  import CarouselComponent from './components/Containers/Carousel/index.vue'
9
9
  // Replace AppHeader with AppHeaderNew to introduce new header
10
- import AppHeader from './components/Global/AppHeader/index.vue'
10
+ import AppHeader from './components/Global/AppHeaderNew/index.vue'
11
11
  import AppFooter from './components/Global/AppFooter/index.vue'
12
12
  import HeroHeader from './components/Global/HeroHeader/index.vue'
13
13
  import SocialShare from './components/Global/SocialShare/index.vue'