cja-phoenix 0.2.3 → 0.2.5
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.
- package/dist/cja-phoenix.es.js +2808 -3294
- package/dist/style.css +1 -1
- package/dist/types/components/composite/CjaMenuBar.vue.d.ts +42 -0
- package/dist/types/components/composite/FunnelLayout.vue.d.ts +35 -0
- package/dist/types/components/composite/FunnelSubmit.vue.d.ts +2 -2
- package/dist/types/components/composite/FunnelSummary.vue.d.ts +14 -9
- package/dist/types/components/composite/FunnelTitle.vue.d.ts +1 -1
- package/dist/types/components/composite/JourneyMacroSteps.vue.d.ts +16 -0
- package/dist/types/components/composite/ProductDetails.vue.d.ts +4 -4
- package/dist/types/components/composite/ResultsLayout.vue.d.ts +11 -0
- package/dist/types/components/forms/CheckboxInput.vue.d.ts +8 -5
- package/dist/types/components/forms/FileInput.vue.d.ts +7 -4
- package/dist/types/components/forms/InputToggle.vue.d.ts +11 -7
- package/dist/types/components/forms/NumberInput.vue.d.ts +6 -3
- package/dist/types/components/forms/PhoneInput.vue.d.ts +18 -16
- package/dist/types/components/forms/RadioInput.vue.d.ts +41 -0
- package/dist/types/components/forms/SelectInput.vue.d.ts +18 -17
- package/dist/types/components/forms/SelectionTiles.vue.d.ts +7 -4
- package/dist/types/components/forms/TextInput.vue.d.ts +20 -18
- package/dist/types/components/forms/TileCheckboxInput.vue.d.ts +1 -1
- package/dist/types/components/forms/structure/InputContainer.vue.d.ts +2 -2
- package/dist/types/components/forms/structure/InputError.vue.d.ts +1 -1
- package/dist/types/components/forms/structure/InputTitle.vue.d.ts +1 -1
- package/dist/types/components/index.d.ts +10 -3
- package/dist/types/components/structural/CjaButton.vue.d.ts +8 -5
- package/dist/types/components/structural/CollapseContainer.vue.d.ts +3 -3
- package/dist/types/components/structural/ContentTabs.vue.d.ts +1 -1
- package/dist/types/components/structural/FixedContainer.vue.d.ts +63 -0
- package/dist/types/components/structural/GridContainer.vue.d.ts +9 -6
- package/dist/types/components/structural/GridItem.vue.d.ts +8 -8
- package/dist/types/components/structural/InfoMessage.vue.d.ts +30 -0
- package/dist/types/components/structural/LoadingSpinner.vue.d.ts +1 -1
- package/dist/types/components/structural/Modal.vue.d.ts +3 -3
- package/dist/types/components/structural/Scaffold.vue.d.ts +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/stories/Modal.story.vue.d.ts +1 -1
- package/dist/types/types/MacroStep.d.ts +5 -0
- package/dist/types/types/index.d.ts +5 -0
- package/package.json +4 -4
- package/src/assets/iconia/demo.html +57 -1
- package/src/assets/iconia/fonts/CGG-icomoon.eot +0 -0
- package/src/assets/iconia/fonts/CGG-icomoon.svg +4 -0
- package/src/assets/iconia/fonts/CGG-icomoon.ttf +0 -0
- package/src/assets/iconia/fonts/CGG-icomoon.woff +0 -0
- package/src/assets/iconia/selection.json +1 -1
- package/src/assets/iconia/style.css +17 -5
- package/src/assets/iconia/style.scss +25 -5
- package/src/assets/iconia/variables.scss +4 -0
- package/src/components/composite/CjaMenuBar.vue +166 -0
- package/src/components/composite/FunnelLayout.vue +194 -0
- package/src/components/composite/FunnelSubmit.vue +20 -11
- package/src/components/composite/FunnelSummary.vue +16 -10
- package/src/components/composite/JourneyMacroSteps.vue +73 -0
- package/src/components/composite/ProductDetails.vue +4 -2
- package/src/components/composite/ResultsLayout.vue +49 -0
- package/src/components/forms/InputToggle.vue +6 -1
- package/src/components/forms/PhoneInput.vue +15 -6
- package/src/components/forms/RadioInput.vue +90 -0
- package/src/components/forms/SelectInput.vue +17 -7
- package/src/components/forms/SelectionTiles.vue +18 -5
- package/src/components/index.ts +16 -2
- package/src/components/structural/CjaButton.vue +14 -9
- package/src/components/structural/CollapseContainer.vue +35 -32
- package/src/components/structural/FixedContainer.vue +87 -0
- package/src/components/structural/InfoMessage.vue +97 -0
- package/src/index.ts +1 -0
- package/src/types/MacroStep.ts +5 -0
- package/src/types/index.ts +6 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="results-container">
|
|
3
|
+
<slot name="mobile-controls" v-if="!activeViewport.lg"></slot>
|
|
4
|
+
<GridContainer class="results-grid">
|
|
5
|
+
<GridItem :size-lg="3" v-if="activeViewport.lg">
|
|
6
|
+
<div class="btn-container">
|
|
7
|
+
<button class="m-cgg-icon--arrow-back"></button>
|
|
8
|
+
</div>
|
|
9
|
+
<slot name="sidebar"></slot>
|
|
10
|
+
</GridItem>
|
|
11
|
+
<GridItem :size-sm="2" :size-md="4" :size-lg="9">
|
|
12
|
+
<slot name="content"></slot>
|
|
13
|
+
</GridItem>
|
|
14
|
+
</GridContainer>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { inject } from "vue";
|
|
20
|
+
import GridContainer from "../structural/GridContainer.vue";
|
|
21
|
+
import GridItem from "../structural/GridItem.vue";
|
|
22
|
+
|
|
23
|
+
const activeViewport: any = inject("activeViewport");
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<style lang="scss" scoped>
|
|
27
|
+
.results-container {
|
|
28
|
+
.results-grid {
|
|
29
|
+
padding-top: 24px;
|
|
30
|
+
padding-bottom: 24px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.btn-container {
|
|
34
|
+
button {
|
|
35
|
+
background: none;
|
|
36
|
+
padding: 0;
|
|
37
|
+
border: none;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
font-size: 26px;
|
|
40
|
+
line-height: 37px;
|
|
41
|
+
margin-bottom: 15px;
|
|
42
|
+
|
|
43
|
+
:focus {
|
|
44
|
+
outline: none;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="input-container">
|
|
3
3
|
<div
|
|
4
4
|
class="input-wrapper"
|
|
5
|
-
:class="[`size-${size}`, { active: modelValue }]"
|
|
5
|
+
:class="[`size-${size}`, { active: modelValue, 'full-width': fullWidth }]"
|
|
6
6
|
@click="$emit('update:modelValue', !modelValue)"
|
|
7
7
|
>
|
|
8
8
|
<div class="label">
|
|
@@ -25,6 +25,7 @@ const props = withDefaults(
|
|
|
25
25
|
validation?: any;
|
|
26
26
|
label: string;
|
|
27
27
|
modelValue: boolean;
|
|
28
|
+
fullWidth: boolean;
|
|
28
29
|
error?: string;
|
|
29
30
|
errorDisplay?: boolean;
|
|
30
31
|
}>(),
|
|
@@ -47,6 +48,10 @@ const emit = defineEmits(["update:modelValue"]);
|
|
|
47
48
|
align-items: center;
|
|
48
49
|
gap: 10px;
|
|
49
50
|
|
|
51
|
+
&.full-width {
|
|
52
|
+
justify-content: space-between;
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
.label {
|
|
51
56
|
@include input-title;
|
|
52
57
|
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
:value="value"
|
|
16
16
|
:autocomplete="autocomplete"
|
|
17
17
|
@input="
|
|
18
|
-
emit(
|
|
18
|
+
emit(
|
|
19
|
+
'update:modelValue',
|
|
20
|
+
Number((<HTMLInputElement>$event.target).value)
|
|
21
|
+
)
|
|
19
22
|
"
|
|
20
23
|
/>
|
|
21
24
|
</div>
|
|
@@ -46,7 +49,7 @@ const props = withDefaults(
|
|
|
46
49
|
modelValue: InputHTMLAttributes["value"];
|
|
47
50
|
id?: InputHTMLAttributes["id"];
|
|
48
51
|
disabled?: InputHTMLAttributes["disabled"];
|
|
49
|
-
phoneCountryCode
|
|
52
|
+
phoneCountryCode?: number;
|
|
50
53
|
autocomplete?: InputHTMLAttributes["autocomplete"];
|
|
51
54
|
}>(),
|
|
52
55
|
{
|
|
@@ -73,14 +76,20 @@ onMounted(() => {
|
|
|
73
76
|
inputEl.value.addEventListener("countrychange", () =>
|
|
74
77
|
emit(
|
|
75
78
|
"update:phoneCountryCode",
|
|
76
|
-
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
+
Number(
|
|
80
|
+
window.intlTelInputGlobals
|
|
81
|
+
.getInstance(inputEl.value)
|
|
82
|
+
.getSelectedCountryData().dialCode
|
|
83
|
+
)
|
|
79
84
|
)
|
|
80
85
|
);
|
|
81
86
|
|
|
82
87
|
intlTelInput(inputEl.value, {
|
|
83
|
-
initialCountry:
|
|
88
|
+
initialCountry: props.phoneCountryCode
|
|
89
|
+
? window.intlTelInputGlobals
|
|
90
|
+
.getCountryData()
|
|
91
|
+
.find((c) => Number(c.dialCode) == props.phoneCountryCode)?.iso2
|
|
92
|
+
: "pt",
|
|
84
93
|
preferredCountries: ["pt"],
|
|
85
94
|
separateDialCode: true,
|
|
86
95
|
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="input-container">
|
|
3
|
+
<div class="input-container-radio">
|
|
4
|
+
<label>
|
|
5
|
+
<input
|
|
6
|
+
type="radio"
|
|
7
|
+
:name="name"
|
|
8
|
+
:checked="modelValue == value"
|
|
9
|
+
@change="handleChange"
|
|
10
|
+
/>
|
|
11
|
+
<div class="radio-icon"></div>
|
|
12
|
+
<div class="text-container" v-html="label"></div>
|
|
13
|
+
</label>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<InputError :error="error" v-if="error && errorDisplay" />
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script lang="ts" setup>
|
|
21
|
+
import InputError from "./structure/InputError.vue";
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(
|
|
24
|
+
defineProps<{
|
|
25
|
+
name: string;
|
|
26
|
+
value: any;
|
|
27
|
+
label: string;
|
|
28
|
+
modelValue?: any;
|
|
29
|
+
error?: string;
|
|
30
|
+
errorDisplay?: boolean;
|
|
31
|
+
}>(),
|
|
32
|
+
{
|
|
33
|
+
errorDisplay: true,
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const handleChange = (event: any) => {
|
|
38
|
+
if (event.target.checked) {
|
|
39
|
+
emit("update:modelValue", props.value);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<style lang="scss" scoped>
|
|
47
|
+
.input-container-radio {
|
|
48
|
+
label {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: row;
|
|
51
|
+
align-items: center;
|
|
52
|
+
flex-wrap: nowrap;
|
|
53
|
+
gap: 15px;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
margin: 0;
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
|
|
58
|
+
input {
|
|
59
|
+
display: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.radio-icon {
|
|
63
|
+
width: 20px;
|
|
64
|
+
height: 20px;
|
|
65
|
+
border: 1px solid #64748b;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
input:checked + .radio-icon {
|
|
70
|
+
border: 6px solid #076b9c;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.text-container {
|
|
74
|
+
font-size: 16px;
|
|
75
|
+
line-height: 19px;
|
|
76
|
+
user-select: none;
|
|
77
|
+
|
|
78
|
+
a {
|
|
79
|
+
color: inherit;
|
|
80
|
+
font-weight: 700;
|
|
81
|
+
text-decoration: underline;
|
|
82
|
+
|
|
83
|
+
&:hover {
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
</style>
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
>
|
|
10
10
|
<div
|
|
11
11
|
class="select-toggle"
|
|
12
|
-
:class="[
|
|
12
|
+
:class="[
|
|
13
|
+
`size-${size}`,
|
|
14
|
+
collapsePosition,
|
|
15
|
+
{ open: open, disabled: disabled },
|
|
16
|
+
]"
|
|
13
17
|
@click="toggleCollapse()"
|
|
14
18
|
>
|
|
15
19
|
<span class="select-display">{{ displayValue || placeholder }}</span>
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
></span>
|
|
45
49
|
{{ option.label }}
|
|
46
50
|
</li>
|
|
47
|
-
<li v-if="searchFilter && filteredOptions
|
|
51
|
+
<li v-if="searchFilter && filteredOptions?.length == 0">
|
|
48
52
|
{{ searchFilter.noResults }}
|
|
49
53
|
</li>
|
|
50
54
|
</ul>
|
|
@@ -86,7 +90,7 @@ const props = withDefaults(
|
|
|
86
90
|
id?: InputHTMLAttributes["id"];
|
|
87
91
|
disabled?: InputHTMLAttributes["disabled"];
|
|
88
92
|
modelValue: SelectHTMLAttributes["value"];
|
|
89
|
-
options: SelectOption[];
|
|
93
|
+
options: SelectOption[] | null;
|
|
90
94
|
multiSelect?: boolean;
|
|
91
95
|
searchFilter?: {
|
|
92
96
|
placeholder: string;
|
|
@@ -108,7 +112,7 @@ const inputEl = ref();
|
|
|
108
112
|
const collapseEl = ref();
|
|
109
113
|
const collapsePosition = ref();
|
|
110
114
|
const filteredOptions = computed(() =>
|
|
111
|
-
search.value
|
|
115
|
+
search.value && props.options
|
|
112
116
|
? props.options.filter((opt) =>
|
|
113
117
|
opt.label.toLowerCase().includes(search.value.toLowerCase())
|
|
114
118
|
)
|
|
@@ -127,7 +131,7 @@ defineExpose({ errorMessage, meta, validate });
|
|
|
127
131
|
const emit = defineEmits(["update:modelValue"]);
|
|
128
132
|
|
|
129
133
|
const displayValue = computed(() =>
|
|
130
|
-
inputValue.value
|
|
134
|
+
inputValue.value && props.options
|
|
131
135
|
? props.multiSelect
|
|
132
136
|
? props.options
|
|
133
137
|
.filter((o) => inputValue.value.includes(o.value))
|
|
@@ -193,7 +197,7 @@ const toggleCollapse = () => {
|
|
|
193
197
|
}
|
|
194
198
|
};
|
|
195
199
|
|
|
196
|
-
const calcPosition = (el:
|
|
200
|
+
const calcPosition = (el: any) => {
|
|
197
201
|
const elRect = el.getBoundingClientRect();
|
|
198
202
|
const inputRect = inputEl.value.getBoundingClientRect();
|
|
199
203
|
const position =
|
|
@@ -252,7 +256,13 @@ const selectValue = (value: string) => {
|
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
&.open {
|
|
255
|
-
|
|
259
|
+
&.position-bottom {
|
|
260
|
+
border-radius: 5px 5px 0 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
&.position-top {
|
|
264
|
+
border-radius: 0 0 5px 5px;
|
|
265
|
+
}
|
|
256
266
|
|
|
257
267
|
em {
|
|
258
268
|
transform: rotate(180deg);
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
class="tile"
|
|
8
8
|
:class="{
|
|
9
9
|
active: multiselect
|
|
10
|
-
?
|
|
11
|
-
: option.value ==
|
|
10
|
+
? value.includes(option.value)
|
|
11
|
+
: option.value == value,
|
|
12
12
|
}"
|
|
13
13
|
@click="$emit('update:modelValue', option.value)"
|
|
14
14
|
v-tippy="option.tooltip ? option.tooltip : ''"
|
|
@@ -72,6 +72,8 @@ defineEmits(["update:modelValue"]);
|
|
|
72
72
|
</script>
|
|
73
73
|
|
|
74
74
|
<style lang="scss" scoped>
|
|
75
|
+
@import "../../assets/shadows.scss";
|
|
76
|
+
|
|
75
77
|
.tiles-container {
|
|
76
78
|
display: grid;
|
|
77
79
|
width: 100%;
|
|
@@ -95,10 +97,20 @@ defineEmits(["update:modelValue"]);
|
|
|
95
97
|
padding-right: 20px;
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
@mixin activeStyle {
|
|
101
|
+
box-shadow: $box-shadow-m;
|
|
102
|
+
background-color: #f4f9fc;
|
|
103
|
+
border-color: #076b9c;
|
|
104
|
+
}
|
|
105
|
+
|
|
99
106
|
&.active {
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
@include activeStyle;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (any-hover: hover) {
|
|
111
|
+
&:hover {
|
|
112
|
+
@include activeStyle;
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
.text-wrapper {
|
|
@@ -167,6 +179,7 @@ defineEmits(["update:modelValue"]);
|
|
|
167
179
|
&.layout-image {
|
|
168
180
|
.tile {
|
|
169
181
|
justify-content: center;
|
|
182
|
+
text-align: center;
|
|
170
183
|
|
|
171
184
|
.image-container img {
|
|
172
185
|
display: block;
|
package/src/components/index.ts
CHANGED
|
@@ -2,31 +2,39 @@ import Modal from "./structural/Modal.vue";
|
|
|
2
2
|
import CjaButton from "./structural/CjaButton.vue";
|
|
3
3
|
import LoadingSpinner from "./structural/LoadingSpinner.vue";
|
|
4
4
|
import ContentTabs from "./structural/ContentTabs.vue";
|
|
5
|
+
import InfoMessage from "./structural/InfoMessage.vue";
|
|
5
6
|
import Scaffold from "./structural/Scaffold.vue";
|
|
6
7
|
import GridContainer from "./structural/GridContainer.vue";
|
|
7
8
|
import GridItem from "./structural/GridItem.vue";
|
|
9
|
+
import CollapseContainer from "./structural/CollapseContainer.vue";
|
|
10
|
+
import FixedContainer from "./structural/FixedContainer.vue";
|
|
8
11
|
|
|
9
12
|
import TextInput from "./forms/TextInput.vue";
|
|
10
13
|
import PhoneInput from "./forms/PhoneInput.vue";
|
|
11
14
|
import CheckboxInput from "./forms/CheckboxInput.vue";
|
|
15
|
+
import RadioInput from "./forms/RadioInput.vue";
|
|
12
16
|
import TileCheckboxInput from "./forms/TileCheckboxInput.vue";
|
|
13
17
|
import SelectInput from "./forms/SelectInput.vue";
|
|
14
18
|
import FileInput from "./forms/FileInput.vue";
|
|
15
19
|
import NumberInput from "./forms/NumberInput.vue";
|
|
16
20
|
import InputToggle from "./forms/InputToggle.vue";
|
|
17
|
-
import CollapseContainer from "./structural/CollapseContainer.vue";
|
|
18
21
|
import SelectionTiles from "./forms/SelectionTiles.vue";
|
|
19
22
|
|
|
20
|
-
import
|
|
23
|
+
import JourneyMacroSteps from "./composite/JourneyMacroSteps.vue";
|
|
24
|
+
import FunnelLayout from "./composite/FunnelLayout.vue";
|
|
21
25
|
import FunnelSubmit from "./composite/FunnelSubmit.vue";
|
|
22
26
|
import FunnelSummary from "./composite/FunnelSummary.vue";
|
|
23
27
|
import FunnelTitle from "./composite/FunnelTitle.vue";
|
|
28
|
+
import ResultsLayout from "./composite/ResultsLayout.vue";
|
|
29
|
+
import ProductDetails from "./composite/ProductDetails.vue";
|
|
30
|
+
import CjaMenuBar from "./composite/CjaMenuBar.vue";
|
|
24
31
|
|
|
25
32
|
export {
|
|
26
33
|
Modal,
|
|
27
34
|
CjaButton,
|
|
28
35
|
TextInput,
|
|
29
36
|
PhoneInput,
|
|
37
|
+
RadioInput,
|
|
30
38
|
CheckboxInput,
|
|
31
39
|
TileCheckboxInput,
|
|
32
40
|
NumberInput,
|
|
@@ -41,7 +49,13 @@ export {
|
|
|
41
49
|
CollapseContainer,
|
|
42
50
|
GridContainer,
|
|
43
51
|
GridItem,
|
|
52
|
+
FunnelLayout,
|
|
44
53
|
FunnelSubmit,
|
|
45
54
|
FunnelSummary,
|
|
46
55
|
FunnelTitle,
|
|
56
|
+
JourneyMacroSteps,
|
|
57
|
+
CjaMenuBar,
|
|
58
|
+
FixedContainer,
|
|
59
|
+
ResultsLayout,
|
|
60
|
+
InfoMessage,
|
|
47
61
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
`btn-size-${size}`,
|
|
7
7
|
`btn-color-${color}`,
|
|
8
8
|
`icon-${iconPosition}`,
|
|
9
|
+
{ 'btn-loading': loading },
|
|
9
10
|
]"
|
|
10
11
|
>
|
|
11
12
|
<Scaffold v-if="!loading">
|
|
@@ -135,7 +136,7 @@ withDefaults(
|
|
|
135
136
|
cursor: auto;
|
|
136
137
|
}
|
|
137
138
|
|
|
138
|
-
.spinner {
|
|
139
|
+
&.btn-loading .spinner {
|
|
139
140
|
border-color: #fff;
|
|
140
141
|
border-top-color: rgba(255, 255, 255, 0.2);
|
|
141
142
|
}
|
|
@@ -165,14 +166,18 @@ withDefaults(
|
|
|
165
166
|
flex-direction: row-reverse;
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
&.btn-loading {
|
|
170
|
+
cursor: auto;
|
|
171
|
+
|
|
172
|
+
.spinner {
|
|
173
|
+
width: 20px;
|
|
174
|
+
height: 20px;
|
|
175
|
+
border-width: 2px;
|
|
176
|
+
border-style: solid;
|
|
177
|
+
border-radius: 50%;
|
|
178
|
+
animation: spin 1s infinite;
|
|
179
|
+
animation-timing-function: linear;
|
|
180
|
+
}
|
|
176
181
|
}
|
|
177
182
|
}
|
|
178
183
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="collapse-container">
|
|
3
|
-
<div
|
|
4
|
-
class="collapse-header"
|
|
5
|
-
@click="active = !active"
|
|
6
|
-
:class="{ active: active }"
|
|
7
|
-
>
|
|
2
|
+
<div class="collapse-container" :class="{ active: active }">
|
|
3
|
+
<div class="collapse-header" @click="active = !active">
|
|
8
4
|
<div class="header-wrapper">
|
|
9
5
|
<slot name="header"></slot>
|
|
10
6
|
</div>
|
|
@@ -14,13 +10,14 @@
|
|
|
14
10
|
name="slide"
|
|
15
11
|
@before-enter="setHeightZero"
|
|
16
12
|
@enter="setHeightSize"
|
|
13
|
+
@after-enter="clearHeight"
|
|
17
14
|
@leave="setHeightZero"
|
|
18
15
|
>
|
|
19
16
|
<div
|
|
20
17
|
v-show="active"
|
|
21
18
|
ref="contentContainer"
|
|
22
19
|
class="content-container"
|
|
23
|
-
:
|
|
20
|
+
:style="{ height: containerHeight, overflow: containerOverflow }"
|
|
24
21
|
>
|
|
25
22
|
<div ref="contentWrapper" class="content-wrapper">
|
|
26
23
|
<slot name="content"></slot>
|
|
@@ -43,11 +40,13 @@ const props = defineProps<{
|
|
|
43
40
|
const active = ref(props.defaultActive);
|
|
44
41
|
const contentContainer = ref();
|
|
45
42
|
const contentWrapper = ref();
|
|
43
|
+
const containerHeight = ref();
|
|
44
|
+
const containerOverflow = ref();
|
|
46
45
|
|
|
47
46
|
const setHeightSize = () => {
|
|
48
47
|
requestAnimationFrame(() => {
|
|
49
48
|
if (contentContainer.value && contentWrapper.value) {
|
|
50
|
-
|
|
49
|
+
containerHeight.value = `${contentWrapper.value.clientHeight}px`;
|
|
51
50
|
|
|
52
51
|
if (active.value && props.scrollToContent) {
|
|
53
52
|
setTimeout(() => {
|
|
@@ -58,24 +57,24 @@ const setHeightSize = () => {
|
|
|
58
57
|
});
|
|
59
58
|
}, 250);
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
setTimeout(() => {
|
|
63
|
-
contentContainer.value.style.height = "";
|
|
64
|
-
contentContainer.value.style.overflow = "visible";
|
|
65
|
-
}, 200);
|
|
66
60
|
}
|
|
67
61
|
});
|
|
68
62
|
};
|
|
69
63
|
|
|
70
64
|
const setHeightZero = () => {
|
|
71
|
-
|
|
65
|
+
containerHeight.value = `${contentWrapper.value.clientHeight}px`;
|
|
72
66
|
|
|
73
67
|
requestAnimationFrame(() => {
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
containerHeight.value = "0";
|
|
69
|
+
containerOverflow.value = "";
|
|
76
70
|
});
|
|
77
71
|
};
|
|
78
72
|
|
|
73
|
+
const clearHeight = () => {
|
|
74
|
+
containerHeight.value = "";
|
|
75
|
+
containerOverflow.value = "visible";
|
|
76
|
+
};
|
|
77
|
+
|
|
79
78
|
onMounted(() => {
|
|
80
79
|
if (props.defaultActive) {
|
|
81
80
|
setHeightSize();
|
|
@@ -90,26 +89,30 @@ onUnmounted(() => {
|
|
|
90
89
|
</script>
|
|
91
90
|
|
|
92
91
|
<style lang="scss" scoped>
|
|
93
|
-
.collapse-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
.collapse-container {
|
|
93
|
+
.collapse-header {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: row;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: space-between;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
user-select: none;
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
> span {
|
|
102
|
+
transition: all 0.2s linear;
|
|
103
|
+
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
.content-container {
|
|
107
|
+
box-sizing: border-box;
|
|
108
|
+
overflow: hidden;
|
|
109
|
+
transition: all 0.2s linear;
|
|
107
110
|
}
|
|
108
|
-
}
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
&.active {
|
|
113
|
+
.collapse-header > span {
|
|
114
|
+
transform: rotate(180deg);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
114
117
|
}
|
|
115
118
|
</style>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="fixed-container"
|
|
4
|
+
:style="{ height: fixedContainerHeight }"
|
|
5
|
+
ref="fixedContainer"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
class="fixed-wrapper"
|
|
9
|
+
:class="{ 'position-fixed': positionFixed }"
|
|
10
|
+
:style="{ ...size, ...position }"
|
|
11
|
+
ref="fixedWrapper"
|
|
12
|
+
>
|
|
13
|
+
<slot></slot>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { onUnmounted, onMounted } from "vue";
|
|
20
|
+
import { ref } from "vue";
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
scrollThreshold?: number;
|
|
25
|
+
fixWidth?: boolean;
|
|
26
|
+
size?: {
|
|
27
|
+
height?: string;
|
|
28
|
+
width?: string;
|
|
29
|
+
};
|
|
30
|
+
position?: {
|
|
31
|
+
left?: string;
|
|
32
|
+
top?: string;
|
|
33
|
+
right?: string;
|
|
34
|
+
bottom?: string;
|
|
35
|
+
};
|
|
36
|
+
}>(),
|
|
37
|
+
{
|
|
38
|
+
scrollThreshold: 0,
|
|
39
|
+
fixWidth: true,
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const positionFixed = ref(false);
|
|
44
|
+
const fixedContainer = ref();
|
|
45
|
+
const fixedContainerHeight = ref("");
|
|
46
|
+
const fixedWrapper = ref();
|
|
47
|
+
|
|
48
|
+
const fixPosition = () => {
|
|
49
|
+
if (fixedContainer.value) {
|
|
50
|
+
positionFixed.value =
|
|
51
|
+
window.scrollY > fixedContainer.value.offsetTop + props.scrollThreshold;
|
|
52
|
+
|
|
53
|
+
fixedContainerHeight.value = positionFixed.value
|
|
54
|
+
? `${fixedWrapper.value.clientHeight}px`
|
|
55
|
+
: "";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const setWidth = () => {
|
|
60
|
+
fixedWrapper.value.style.maxWidth = `${fixedContainer.value.offsetWidth}px`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
onMounted(() => {
|
|
64
|
+
window.addEventListener("scroll", fixPosition);
|
|
65
|
+
fixPosition();
|
|
66
|
+
|
|
67
|
+
if (props.fixWidth) {
|
|
68
|
+
window.addEventListener("resize", setWidth);
|
|
69
|
+
setWidth();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
onUnmounted(() => {
|
|
74
|
+
window.removeEventListener("scroll", fixPosition);
|
|
75
|
+
window.removeEventListener("resize", setWidth);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
defineExpose({ positionFixed });
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<style lang="scss" scoped>
|
|
82
|
+
.fixed-wrapper {
|
|
83
|
+
&.position-fixed {
|
|
84
|
+
position: fixed;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
</style>
|