cja-phoenix 0.2.1 → 0.2.3

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 (33) hide show
  1. package/dist/cja-phoenix.es.js +2805 -2529
  2. package/dist/style.css +1 -1
  3. package/dist/types/components/composite/FunnelSubmit.vue.d.ts +24 -0
  4. package/dist/types/components/composite/FunnelSummary.vue.d.ts +72 -0
  5. package/dist/types/components/composite/FunnelTitle.vue.d.ts +19 -0
  6. package/dist/types/components/forms/CheckboxInput.vue.d.ts +2 -2
  7. package/dist/types/components/forms/FileInput.vue.d.ts +2 -2
  8. package/dist/types/components/forms/InputToggle.vue.d.ts +1 -1
  9. package/dist/types/components/forms/NumberInput.vue.d.ts +55 -0
  10. package/dist/types/components/forms/PhoneInput.vue.d.ts +2 -2
  11. package/dist/types/components/forms/SelectInput.vue.d.ts +2 -2
  12. package/dist/types/components/forms/SelectionTiles.vue.d.ts +33 -5
  13. package/dist/types/components/forms/TextInput.vue.d.ts +2 -6
  14. package/dist/types/components/index.d.ts +8 -3
  15. package/dist/types/components/structural/CjaButton.vue.d.ts +4 -2
  16. package/dist/types/components/structural/GridItem.vue.d.ts +6 -6
  17. package/dist/types/types/TileOption.d.ts +3 -2
  18. package/dist/types/utils/RouteGenerator.d.ts +1 -1
  19. package/package.json +1 -1
  20. package/src/components/composite/FunnelSubmit.vue +42 -0
  21. package/src/components/composite/FunnelSummary.vue +192 -0
  22. package/src/components/composite/FunnelTitle.vue +57 -0
  23. package/src/components/forms/CheckboxInput.vue +3 -3
  24. package/src/components/forms/NumberInput.vue +165 -0
  25. package/src/components/forms/SelectInput.vue +5 -10
  26. package/src/components/forms/SelectionTiles.vue +150 -73
  27. package/src/components/forms/TextInput.vue +0 -2
  28. package/src/components/index.ts +12 -2
  29. package/src/components/structural/CjaButton.vue +50 -11
  30. package/src/components/structural/GridItem.vue +17 -17
  31. package/src/types/TileOption.ts +3 -2
  32. package/src/utils/RouteGenerator.ts +1 -1
  33. package/src/utils/ViewportDetector.ts +10 -4
@@ -1,105 +1,182 @@
1
1
  <template>
2
- <div class="tiles-container">
3
- <div
4
- v-for="(option, idx) in options"
5
- :key="idx"
6
- class="tile"
7
- :class="{ active: option.value === modelValue }"
8
- @click="$emit('update:modelValue', option.value)"
9
- v-tippy="option.tooltip ? option.tooltip : ''"
10
- >
11
- <div class="image-container" v-if="option.image">
12
- <img :src="`${imageUrl}${option.image}`" :alt="option.title" />
13
- </div>
2
+ <InputContainer :layout="'vertical'">
3
+ <div class="tiles-container" :class="[`layout-${layout}`]">
4
+ <div
5
+ v-for="(option, idx) in options"
6
+ :key="idx"
7
+ class="tile"
8
+ :class="{
9
+ active: multiselect
10
+ ? modelValue.includes(option.value)
11
+ : option.value == modelValue,
12
+ }"
13
+ @click="$emit('update:modelValue', option.value)"
14
+ v-tippy="option.tooltip ? option.tooltip : ''"
15
+ >
16
+ <div class="image-container" v-if="option.image">
17
+ <img :src="option.image" :alt="option.label" />
18
+ </div>
14
19
 
15
- <div class="text-wrapper">
16
- <span class="title">{{ option.title }}</span>
17
- <span class="description" v-if="option.description">
18
- {{ option.description }}
19
- </span>
20
+ <div
21
+ class="text-wrapper"
22
+ v-if="layout != 'image' || (layout == 'image' && !option.image)"
23
+ >
24
+ <span class="title">{{ option.label }}</span>
25
+ <span class="description" v-if="option.description">
26
+ {{ option.description }}
27
+ </span>
28
+ <div
29
+ class="long-description"
30
+ v-if="option.descriptionLong"
31
+ v-html="option.descriptionLong"
32
+ ></div>
33
+ </div>
34
+ <span v-if="layout == 'list'" class="m-cgg-icon--chevron-right"></span>
20
35
  </div>
21
- <span class="m-cgg-icon--chevron-right"></span>
22
36
  </div>
23
- </div>
37
+ <InputError :error="errorMessage" v-if="errorMessage && errorDisplay" />
38
+ </InputContainer>
24
39
  </template>
25
40
 
26
41
  <script lang="ts" setup>
42
+ import { useField } from "vee-validate";
27
43
  import { TileOption } from "../../types/TileOption";
44
+ import InputError from "./structure/InputError.vue";
45
+ import InputContainer from "./structure/InputContainer.vue";
46
+
47
+ const props = withDefaults(
48
+ defineProps<{
49
+ options: TileOption[];
50
+ modelValue: any;
51
+ layout?: "list" | "grid" | "image";
52
+ multiselect?: boolean;
53
+ error?: string;
54
+ validation?: any;
55
+ errorDisplay?: boolean;
56
+ }>(),
57
+ {
58
+ layout: "list",
59
+ errorDisplay: true,
60
+ }
61
+ );
28
62
 
29
- const props = defineProps<{
30
- options: TileOption[];
31
- modelValue: [String, Number, Boolean];
32
- }>();
63
+ const { value, errorMessage, meta, validate } = useField(
64
+ "input",
65
+ props.validation,
66
+ { initialValue: props.modelValue }
67
+ );
33
68
 
34
- const imageUrl = process.env.VUE_APP_IMG_URL;
69
+ defineExpose({ errorMessage, meta, validate });
35
70
 
36
- const emit = defineEmits(["update:modelValue"]);
71
+ defineEmits(["update:modelValue"]);
37
72
  </script>
38
73
 
39
74
  <style lang="scss" scoped>
40
75
  .tiles-container {
41
- display: flex;
42
- flex-direction: column;
43
- gap: 15px;
44
- }
76
+ display: grid;
77
+ width: 100%;
78
+ gap: 16px;
45
79
 
46
- .tile {
47
- display: flex;
48
- flex-direction: row;
49
- align-items: center;
50
- gap: 15px;
51
- padding: 20px;
52
- border: 1px solid #dedede;
53
- border-radius: 10px;
54
- cursor: pointer;
55
- user-select: none;
56
- overflow-x: hidden;
57
- color: #0d2745;
58
- transition: all 0.3s ease-in-out;
59
-
60
- @media screen and (min-width: 992px) {
61
- padding-right: 20px;
62
- }
80
+ .tile {
81
+ display: flex;
82
+ flex-direction: row;
83
+ align-items: center;
84
+ gap: 15px;
85
+ padding: 20px;
86
+ border: 1px solid #dedede;
87
+ border-radius: 10px;
88
+ cursor: pointer;
89
+ user-select: none;
90
+ overflow-x: hidden;
91
+ color: #0d2745;
92
+ transition: all 0.3s ease-in-out;
93
+
94
+ @media screen and (min-width: 992px) {
95
+ padding-right: 20px;
96
+ }
97
+
98
+ &:hover,
99
+ &.active {
100
+ color: #076b9c;
101
+ box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.1);
102
+ }
103
+
104
+ .text-wrapper {
105
+ font-size: 18px;
106
+ line-height: 22px;
107
+
108
+ .title {
109
+ font-weight: 700;
110
+ }
111
+ }
63
112
 
64
- &:hover,
65
- &.active {
66
- color: #076b9c;
67
- box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.1);
113
+ .m-cgg-icon--chevron-right {
114
+ background: none;
115
+ border-radius: 0;
116
+ padding: 0;
117
+ color: inherit;
118
+ margin: 0;
119
+ font-size: 18px;
120
+ font-weight: 700;
121
+ margin-left: auto;
122
+ }
68
123
  }
69
124
 
70
- .image-container img {
71
- height: 28px;
72
- width: 28px;
125
+ &.layout-list,
126
+ &.layout-grid {
127
+ .image-container img {
128
+ height: 28px;
129
+ width: 28px;
73
130
 
74
- @media screen and (min-width: 992px) {
75
- height: 32px;
76
- width: 32px;
131
+ @media screen and (min-width: 992px) {
132
+ height: 32px;
133
+ width: 32px;
134
+ }
77
135
  }
78
136
  }
79
137
 
80
- .text-wrapper {
81
- font-size: 18px;
82
- line-height: 22px;
138
+ &.layout-list {
139
+ grid-template-columns: 1fr;
83
140
 
84
- .title {
85
- font-weight: 700;
141
+ .text-wrapper {
142
+ .description:before {
143
+ content: "-";
144
+ margin: 0 5px;
145
+ }
86
146
  }
147
+ }
87
148
 
88
- .description:before {
89
- content: "-";
90
- margin: 0 5px;
149
+ &.layout-grid {
150
+ .text-wrapper {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 5px;
154
+
155
+ .title {
156
+ font-size: 15px;
157
+ line-height: 18px;
158
+ }
159
+
160
+ .description {
161
+ font-size: 12px;
162
+ line-height: 12px;
163
+ }
91
164
  }
92
165
  }
93
166
 
94
- .m-cgg-icon--chevron-right {
95
- background: none;
96
- border-radius: 0;
97
- padding: 0;
98
- color: inherit;
99
- margin: 0;
100
- font-size: 18px;
101
- font-weight: 700;
102
- margin-left: auto;
167
+ &.layout-image {
168
+ .tile {
169
+ justify-content: center;
170
+
171
+ .image-container img {
172
+ display: block;
173
+ max-width: 100%;
174
+ }
175
+ }
103
176
  }
104
177
  }
178
+
179
+ .input-error {
180
+ text-align: center;
181
+ }
105
182
  </style>
@@ -49,7 +49,6 @@ const props = withDefaults(
49
49
  placeholder?: InputHTMLAttributes["placeholder"];
50
50
  layout?: "vertical" | "horizontal";
51
51
  size?: "sm" | "md" | "lg";
52
- error?: string;
53
52
  validation?: any;
54
53
  errorDisplay?: boolean;
55
54
  modelValue: InputHTMLAttributes["value"];
@@ -61,7 +60,6 @@ const props = withDefaults(
61
60
  type?: InputHTMLAttributes["type"];
62
61
  autocomplete?: InputHTMLAttributes["autocomplete"];
63
62
  suffix?: string;
64
- prefix?: string;
65
63
  }>(),
66
64
  {
67
65
  layout: "vertical",
@@ -3,6 +3,8 @@ import CjaButton from "./structural/CjaButton.vue";
3
3
  import LoadingSpinner from "./structural/LoadingSpinner.vue";
4
4
  import ContentTabs from "./structural/ContentTabs.vue";
5
5
  import Scaffold from "./structural/Scaffold.vue";
6
+ import GridContainer from "./structural/GridContainer.vue";
7
+ import GridItem from "./structural/GridItem.vue";
6
8
 
7
9
  import TextInput from "./forms/TextInput.vue";
8
10
  import PhoneInput from "./forms/PhoneInput.vue";
@@ -10,12 +12,15 @@ import CheckboxInput from "./forms/CheckboxInput.vue";
10
12
  import TileCheckboxInput from "./forms/TileCheckboxInput.vue";
11
13
  import SelectInput from "./forms/SelectInput.vue";
12
14
  import FileInput from "./forms/FileInput.vue";
15
+ import NumberInput from "./forms/NumberInput.vue";
13
16
  import InputToggle from "./forms/InputToggle.vue";
14
17
  import CollapseContainer from "./structural/CollapseContainer.vue";
15
- import GridContainer from "./structural/GridContainer.vue";
16
- import GridItem from "./structural/GridItem.vue";
18
+ import SelectionTiles from "./forms/SelectionTiles.vue";
17
19
 
18
20
  import ProductDetails from "./composite/ProductDetails.vue";
21
+ import FunnelSubmit from "./composite/FunnelSubmit.vue";
22
+ import FunnelSummary from "./composite/FunnelSummary.vue";
23
+ import FunnelTitle from "./composite/FunnelTitle.vue";
19
24
 
20
25
  export {
21
26
  Modal,
@@ -24,7 +29,9 @@ export {
24
29
  PhoneInput,
25
30
  CheckboxInput,
26
31
  TileCheckboxInput,
32
+ NumberInput,
27
33
  SelectInput,
34
+ SelectionTiles,
28
35
  LoadingSpinner,
29
36
  Scaffold,
30
37
  ProductDetails,
@@ -34,4 +41,7 @@ export {
34
41
  CollapseContainer,
35
42
  GridContainer,
36
43
  GridItem,
44
+ FunnelSubmit,
45
+ FunnelSummary,
46
+ FunnelTitle,
37
47
  };
@@ -1,15 +1,24 @@
1
1
  <template>
2
2
  <button
3
3
  class="cja-btn"
4
- :class="[`btn-${type}`, `btn-size-${size}`, `btn-color-${color}`]"
4
+ :class="[
5
+ `btn-${type}`,
6
+ `btn-size-${size}`,
7
+ `btn-color-${color}`,
8
+ `icon-${iconPosition}`,
9
+ ]"
5
10
  >
6
- <span v-if="icon && iconPosition == 'left'" :class="icon"></span>
7
- <span><slot></slot></span>
8
- <span v-if="icon && iconPosition == 'right'" :class="icon"></span>
11
+ <Scaffold v-if="!loading">
12
+ <span><slot></slot></span>
13
+ <span v-if="icon" :class="icon"></span>
14
+ </Scaffold>
15
+ <div class="spinner" v-else></div>
9
16
  </button>
10
17
  </template>
11
18
 
12
19
  <script lang="ts" setup>
20
+ import { Scaffold } from "..";
21
+
13
22
  withDefaults(
14
23
  defineProps<{
15
24
  type?: "primary" | "secondary" | "tertiary";
@@ -17,6 +26,7 @@ withDefaults(
17
26
  size?: "sm" | "md" | "lg";
18
27
  icon?: string;
19
28
  iconPosition?: "left" | "right";
29
+ loading?: boolean;
20
30
  }>(),
21
31
  {
22
32
  type: "primary",
@@ -32,6 +42,7 @@ withDefaults(
32
42
 
33
43
  .cja-btn {
34
44
  display: inline-flex;
45
+ flex-direction: row;
35
46
  justify-content: center;
36
47
  align-items: center;
37
48
  box-shadow: $box-shadow-s;
@@ -123,27 +134,55 @@ withDefaults(
123
134
  color: #9fabbc;
124
135
  cursor: auto;
125
136
  }
137
+
138
+ .spinner {
139
+ border-color: #fff;
140
+ border-top-color: rgba(255, 255, 255, 0.2);
141
+ }
126
142
  }
127
143
 
128
144
  &.btn-secondary {
129
145
  // Implement bordered buttons
130
146
 
131
- .btn-color-blue {
147
+ &.btn-color-blue:not(:disabled) {
132
148
  border-color: #076b9c;
133
149
  color: #076b9c;
134
150
 
135
151
  &:hover {
136
- background-color: #0d2745;
137
- border-color: #0d2745;
138
- color: #fff;
152
+ background-color: #f4f9fc;
153
+ border-color: #155072;
154
+ color: #155072;
139
155
  }
140
156
 
141
157
  &:focus {
142
- background-color: #155072;
143
- border-color: #155072;
144
- color: #fff;
158
+ border-color: #0d2745;
159
+ color: #0d2745;
145
160
  }
146
161
  }
147
162
  }
163
+
164
+ &.icon-left {
165
+ flex-direction: row-reverse;
166
+ }
167
+
168
+ .spinner {
169
+ width: 20px;
170
+ height: 20px;
171
+ border-width: 2px;
172
+ border-style: solid;
173
+ border-radius: 50%;
174
+ animation: spin 1s infinite;
175
+ animation-timing-function: linear;
176
+ }
177
+ }
178
+
179
+ @keyframes spin {
180
+ from {
181
+ transform: rotate(0deg);
182
+ }
183
+
184
+ to {
185
+ transform: rotate(360deg);
186
+ }
148
187
  }
149
188
  </style>
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <div
3
- :class="[
4
- `grid-item-sm-${sizeSm}`,
5
- `grid-item-md-${sizeMd}`,
6
- `grid-item-lg-${sizeLg}`,
7
- ]"
3
+ :class="{
4
+ [`grid-item-sm-${sizeSm}`]: sizeSm,
5
+ [`grid-item-md-${sizeMd}`]: sizeMd,
6
+ [`grid-item-lg-${sizeLg}`]: sizeLg,
7
+ }"
8
8
  >
9
9
  <slot></slot>
10
10
  </div>
@@ -12,33 +12,33 @@
12
12
 
13
13
  <script lang="ts" setup>
14
14
  defineProps<{
15
- sizeSm: 1 | 2 | 3 | 4;
16
- sizeMd: 1 | 2 | 3 | 4;
17
- sizeLg: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
15
+ sizeSm?: 1 | 2 | 3 | 4;
16
+ sizeMd?: 1 | 2 | 3 | 4;
17
+ sizeLg?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
18
18
  }>();
19
19
  </script>
20
20
 
21
21
  <style lang="scss" scoped>
22
- @for $sm from 1 to 5 {
23
- .grid-item-sm-#{$sm} {
22
+ @for $i from 0 to 4 {
23
+ .grid-item-sm-#{$i + 1} {
24
24
  @media screen and (max-width: 767px) {
25
- grid-column: span #{$sm};
25
+ grid-column: span #{$i + 1};
26
26
  }
27
27
  }
28
28
  }
29
29
 
30
- @for $md from 1 to 5 {
31
- .grid-item-md-#{$md} {
30
+ @for $i from 0 to 4 {
31
+ .grid-item-md-#{$i + 1} {
32
32
  @media screen and (min-width: 768px) and (max-width: 1023px) {
33
- grid-column: span #{$md};
33
+ grid-column: span #{$i + 1};
34
34
  }
35
35
  }
36
36
  }
37
37
 
38
- @for $lg from 1 to 13 {
39
- .grid-item-lg-#{$lg} {
38
+ @for $i from 0 to 12 {
39
+ .grid-item-lg-#{$i + 1} {
40
40
  @media screen and (min-width: 1024px) {
41
- grid-column: span #{$lg};
41
+ grid-column: span #{$i + 1};
42
42
  }
43
43
  }
44
44
  }
@@ -1,7 +1,8 @@
1
1
  export interface TileOption {
2
- value: [string, number, boolean];
3
- title: string;
2
+ value: any;
3
+ label: string;
4
4
  description?: string;
5
+ descriptionLong?: string;
5
6
  tooltip?: string;
6
7
  image?: string;
7
8
  }
@@ -1,6 +1,6 @@
1
1
  import { RouteRecordRaw } from "vue-router";
2
2
 
3
- export const generateRoutes = (routes: RouteRecordRaw[]) => {
3
+ export const generateRoutes = (routes: any[]) => {
4
4
  let assembledRoutes: RouteRecordRaw[] = [
5
5
  {
6
6
  path: "/",
@@ -1,11 +1,11 @@
1
- import { Ref, ref } from "vue";
1
+ import { Ref, onMounted, onUnmounted, ref } from "vue";
2
2
 
3
3
  export const useViewportDetector = (
4
4
  breakpoints: { [index: string]: number } = {
5
5
  xs: 0,
6
6
  s: 420,
7
7
  m: 768,
8
- lg: 992,
8
+ lg: 1024,
9
9
  xl: 1200,
10
10
  }
11
11
  ) => {
@@ -17,8 +17,14 @@ export const useViewportDetector = (
17
17
  }
18
18
  };
19
19
 
20
- window.addEventListener("resize", setViewports);
21
- setViewports();
20
+ onMounted(() => {
21
+ window.addEventListener("resize", setViewports);
22
+ setViewports();
23
+ });
24
+
25
+ onUnmounted(() => {
26
+ window.removeEventListener("resize", setViewports);
27
+ });
22
28
 
23
29
  return { activeViewport };
24
30
  };