@propelinc/citrus-ui 0.3.0 → 0.3.1
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/citrus-ui.common.js +1105 -1094
- package/dist/citrus-ui.common.js.map +1 -1
- package/dist/citrus-ui.css +1 -1
- package/dist/citrus-ui.umd.js +1105 -1094
- package/dist/citrus-ui.umd.js.map +1 -1
- package/dist/citrus-ui.umd.min.js +4 -4
- package/dist/citrus-ui.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/components/CCard.vue +77 -10
- package/src/components/CModal.vue +102 -0
- package/src/components/CModalLoading.vue +4 -42
- package/src/styles/mixins/buttons.less +22 -0
- package/src/styles/variables.less +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@propelinc/citrus-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/propelinc/citrus-ui"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"lint:css": "stylelint \"src/**/*.(vue|less)\"",
|
|
15
15
|
"lint:js": "vue-cli-service lint",
|
|
16
16
|
"publish:dist": "npm run build:dist && npm publish",
|
|
17
|
-
"publish:storybook": "storybook-to-aws-s3 --script=\"storybook
|
|
17
|
+
"publish:storybook": "storybook-to-aws-s3 --script=\"build:storybook\" --bucket-path=citrus-ui-storybook",
|
|
18
18
|
"serve:storybook": "vue-cli-service storybook:serve -p 6006 -c .storybook",
|
|
19
19
|
"test:unit": "vue-cli-service test:unit"
|
|
20
20
|
},
|
package/src/components/CCard.vue
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="card">
|
|
3
|
-
<div v-if="
|
|
2
|
+
<div class="card" :class="{ [color]: !!color }">
|
|
3
|
+
<div v-if="shouldShowHeader" class="card__header" :class="{ 'card__header--divided': divided }">
|
|
4
|
+
<div class="card__header__title-container">
|
|
5
|
+
<span data-test="card-title" class="card__header__title">
|
|
6
|
+
<slot name="header:title">
|
|
7
|
+
{{ title }}
|
|
8
|
+
</slot>
|
|
9
|
+
</span>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="card__header__append">
|
|
13
|
+
<slot name="header:append" />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
4
17
|
<div data-test="card-body" class="card__body" :class="{ 'card__body--fluid': fluid }">
|
|
5
18
|
<slot />
|
|
6
19
|
</div>
|
|
20
|
+
|
|
21
|
+
<div class="card__footer" :class="{ 'card__footer--divided': divided }">
|
|
22
|
+
<slot name="footer" />
|
|
23
|
+
</div>
|
|
7
24
|
</div>
|
|
8
25
|
</template>
|
|
9
26
|
|
|
@@ -13,38 +30,88 @@ import { Component, Vue, Prop } from 'vue-property-decorator';
|
|
|
13
30
|
@Component({ name: 'CCard' })
|
|
14
31
|
export default class CCard extends Vue {
|
|
15
32
|
@Prop(String) title?: string;
|
|
33
|
+
/** Removes padding from card body */
|
|
16
34
|
@Prop({ type: Boolean, default: false }) fluid!: boolean;
|
|
35
|
+
/** Shows borders between header, body, and footer */
|
|
36
|
+
@Prop({ type: Boolean, default: false }) divided!: boolean;
|
|
37
|
+
/** Sets background color */
|
|
38
|
+
@Prop(String) color?: string;
|
|
39
|
+
|
|
40
|
+
get shouldShowHeader(): boolean {
|
|
41
|
+
return (
|
|
42
|
+
!!this.title ||
|
|
43
|
+
!!this.$slots['header:append'] ||
|
|
44
|
+
!!this.$scopedSlots['header:append'] ||
|
|
45
|
+
!!this.$slots['header:title'] ||
|
|
46
|
+
!!this.$scopedSlots['header:title']
|
|
47
|
+
);
|
|
48
|
+
}
|
|
17
49
|
}
|
|
18
50
|
</script>
|
|
19
51
|
|
|
20
52
|
<style lang="less" scoped>
|
|
21
53
|
@import '~@/styles/variables.less';
|
|
54
|
+
@import '~@/styles/mixins/buttons.less';
|
|
22
55
|
|
|
23
56
|
.card {
|
|
24
57
|
background: @color-white;
|
|
25
58
|
border: @border;
|
|
26
59
|
border-radius: @border-radius;
|
|
60
|
+
overflow: hidden;
|
|
27
61
|
|
|
28
62
|
& + & {
|
|
29
63
|
margin-top: 12px;
|
|
30
64
|
}
|
|
31
65
|
}
|
|
32
66
|
|
|
33
|
-
.
|
|
34
|
-
|
|
67
|
+
.card__header {
|
|
68
|
+
align-items: flex-start;
|
|
69
|
+
display: flex;
|
|
70
|
+
padding: @card-header-v-spacing @card-h-spacing 0;
|
|
71
|
+
}
|
|
35
72
|
|
|
36
|
-
|
|
73
|
+
.card__header__title-container {
|
|
74
|
+
flex: 1 1 100%;
|
|
37
75
|
}
|
|
38
76
|
|
|
39
|
-
.
|
|
40
|
-
|
|
77
|
+
.card__header__title {
|
|
78
|
+
.title();
|
|
41
79
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
vertical-align: -4px;
|
|
81
|
+
word-break: break-word;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.card__header__append:not(:empty) {
|
|
85
|
+
flex: none;
|
|
86
|
+
margin: -6px -12px -8px @card-h-spacing;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.card__header--divided {
|
|
90
|
+
border-bottom: @border;
|
|
91
|
+
padding-bottom: @card-header-v-spacing;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.card__body {
|
|
95
|
+
padding: @card-body-v-spacing @card-h-spacing;
|
|
45
96
|
}
|
|
46
97
|
|
|
47
98
|
.card__body--fluid {
|
|
48
99
|
padding: 0;
|
|
49
100
|
}
|
|
101
|
+
|
|
102
|
+
.card__footer {
|
|
103
|
+
.button-container();
|
|
104
|
+
|
|
105
|
+
&:not(:empty) {
|
|
106
|
+
margin-top: -1 * @card-body-v-spacing;
|
|
107
|
+
padding: 0 @card-h-spacing;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.card__footer--divided {
|
|
112
|
+
&:not(:empty) {
|
|
113
|
+
border-top: @border;
|
|
114
|
+
margin-top: 0;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
50
117
|
</style>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog
|
|
3
|
+
:width="loading ? '160' : undefined"
|
|
4
|
+
:persistent="!dismissible || !!loading"
|
|
5
|
+
no-click-animation
|
|
6
|
+
:value="value"
|
|
7
|
+
@input="(value) => $emit('input', value)"
|
|
8
|
+
>
|
|
9
|
+
<c-card v-if="loading" key="loading" data-test="modal-loading">
|
|
10
|
+
<div class="modal__loading">
|
|
11
|
+
<div class="modal__loading__icon-container">
|
|
12
|
+
<font-awesome-icon :icon="faSync" class="fa-spin modal__loading__icon" />
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div
|
|
16
|
+
v-if="hasLoadingMessage"
|
|
17
|
+
data-test="modal-loading-message"
|
|
18
|
+
class="modal__loading__message"
|
|
19
|
+
>
|
|
20
|
+
{{ loading }}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</c-card>
|
|
24
|
+
<c-card v-else key="default" data-test="modal">
|
|
25
|
+
<template #header:title>
|
|
26
|
+
<span class="modal__header__title" data-test="modal-title">{{ title }}</span>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template #header:append>
|
|
30
|
+
<c-icon-button
|
|
31
|
+
v-if="dismissible"
|
|
32
|
+
data-test="modal-dismiss"
|
|
33
|
+
:icon="faTimes"
|
|
34
|
+
@click="$emit('input', false)"
|
|
35
|
+
/>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<slot />
|
|
39
|
+
|
|
40
|
+
<template #footer>
|
|
41
|
+
<slot name="footer" />
|
|
42
|
+
</template>
|
|
43
|
+
</c-card>
|
|
44
|
+
</v-dialog>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
import { faSync, faTimes } from '@fortawesome/pro-light-svg-icons';
|
|
49
|
+
import { Component, Vue, Prop } from 'vue-property-decorator';
|
|
50
|
+
|
|
51
|
+
import CCard from '@/components/CCard.vue';
|
|
52
|
+
import CIconButton from '@/components/CIconButton.vue';
|
|
53
|
+
|
|
54
|
+
@Component({ name: 'CModal', components: { CCard, CIconButton } })
|
|
55
|
+
export default class CModal extends Vue {
|
|
56
|
+
faSync = faSync;
|
|
57
|
+
faTimes = faTimes;
|
|
58
|
+
|
|
59
|
+
@Prop({ type: Boolean, default: false }) value!: boolean;
|
|
60
|
+
@Prop(String) title?: string;
|
|
61
|
+
/** Shows loading state + sets loading message if string */
|
|
62
|
+
@Prop([String, Boolean]) loading?: string | boolean;
|
|
63
|
+
/** Allows modal to be dismissed */
|
|
64
|
+
@Prop({ type: Boolean, default: true }) dismissible!: boolean;
|
|
65
|
+
|
|
66
|
+
get hasLoadingMessage(): boolean {
|
|
67
|
+
return typeof this.loading === 'string';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<style lang="less" scoped>
|
|
73
|
+
@import '~@/styles/variables.less';
|
|
74
|
+
|
|
75
|
+
.modal__header__title {
|
|
76
|
+
.subheadline();
|
|
77
|
+
|
|
78
|
+
vertical-align: -4px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.modal__loading {
|
|
82
|
+
text-align: center;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.modal__loading__icon-container {
|
|
86
|
+
padding: 12px 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.modal__loading__icon {
|
|
90
|
+
color: @color-accent-blue;
|
|
91
|
+
font-size: @font-size-2x-large;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.modal__loading__message {
|
|
95
|
+
font-weight: @font-weight-bold;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/deep/ .card__footer:not(:empty) {
|
|
99
|
+
margin-top: -12px;
|
|
100
|
+
padding-bottom: 4px;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -1,54 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
data-test="modal-loading"
|
|
4
|
-
width="160"
|
|
5
|
-
persistent
|
|
6
|
-
no-click-animation
|
|
7
|
-
:value="value"
|
|
8
|
-
@input="(value) => $emit('input', value)"
|
|
9
|
-
>
|
|
10
|
-
<v-card class="modal-loading__content">
|
|
11
|
-
<font-awesome-icon :icon="faSync" class="fa-spin modal-loading__icon" />
|
|
12
|
-
|
|
13
|
-
<div data-test="modal-loading-message" class="modal-loading__content__message">
|
|
14
|
-
<slot>
|
|
15
|
-
{{ message }}
|
|
16
|
-
</slot>
|
|
17
|
-
</div>
|
|
18
|
-
</v-card>
|
|
19
|
-
</v-dialog>
|
|
2
|
+
<c-modal :loading="message || true" :value="value" @input="(value) => $emit('input', value)" />
|
|
20
3
|
</template>
|
|
21
4
|
|
|
22
5
|
<script lang="ts">
|
|
23
|
-
import { faSync } from '@fortawesome/pro-light-svg-icons';
|
|
24
6
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
25
7
|
import { Component, Vue, Prop } from 'vue-property-decorator';
|
|
26
8
|
|
|
27
|
-
|
|
28
|
-
export default class CModalLoading extends Vue {
|
|
29
|
-
faSync = faSync;
|
|
9
|
+
import CModal from '@/components/CModal.vue';
|
|
30
10
|
|
|
11
|
+
@Component({ name: 'CModalLoading', components: { FontAwesomeIcon, CModal } })
|
|
12
|
+
export default class CModalLoading extends Vue {
|
|
31
13
|
@Prop({ type: Boolean, default: false }) value!: boolean;
|
|
32
14
|
@Prop({ type: String }) message?: string;
|
|
33
15
|
}
|
|
34
16
|
</script>
|
|
35
|
-
|
|
36
|
-
<style lang="less" scoped>
|
|
37
|
-
@import '~@/styles/variables.less';
|
|
38
|
-
|
|
39
|
-
.modal-loading__icon {
|
|
40
|
-
color: @color-accent-blue;
|
|
41
|
-
font-size: @font-size-2x-large;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.modal-loading__content {
|
|
45
|
-
border-radius: @border-radius !important; // Override Vuetify styles
|
|
46
|
-
padding: 28px 16px 20px;
|
|
47
|
-
text-align: center;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.modal-loading__content__message {
|
|
51
|
-
font-weight: @font-weight-bold;
|
|
52
|
-
margin: 12px 0 0;
|
|
53
|
-
}
|
|
54
|
-
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.button-container() {
|
|
2
|
+
align-items: stretch;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
|
|
6
|
+
& /deep/ .button--primary,
|
|
7
|
+
& /deep/ .button--secondary {
|
|
8
|
+
margin-top: 12px;
|
|
9
|
+
|
|
10
|
+
&:last-child {
|
|
11
|
+
margin-bottom: 12px;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
& /deep/ .button--tertiary {
|
|
16
|
+
margin-top: 8px;
|
|
17
|
+
|
|
18
|
+
&:last-child {
|
|
19
|
+
margin-bottom: 8px;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -98,6 +98,11 @@
|
|
|
98
98
|
text-transform: uppercase;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// CARDS
|
|
102
|
+
@card-body-v-spacing: 16px;
|
|
103
|
+
@card-h-spacing: 16px;
|
|
104
|
+
@card-header-v-spacing: 12px;
|
|
105
|
+
|
|
101
106
|
// SKELETON LOADER
|
|
102
107
|
@skeleton-loader-animation-duration: 3s;
|
|
103
108
|
@skeleton-loader-animation: skeleton-loader-animation @skeleton-loader-animation-duration infinite;
|