@propelinc/citrus-ui 0.3.0 → 0.3.2
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 +757 -734
- package/dist/citrus-ui.common.js.map +1 -1
- package/dist/citrus-ui.css +1 -1
- package/dist/citrus-ui.umd.js +757 -734
- 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/CButton.vue +4 -0
- package/src/components/CCard.vue +79 -8
- package/src/components/CModal.vue +104 -0
- package/src/components/CModalLoading.vue +4 -42
- package/src/components/helpers/FormField.vue +7 -1
- package/src/index.d.ts +1 -0
- package/src/index.ts +2 -0
- package/src/styles/form-fields.less +1 -0
- 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.2",
|
|
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
|
},
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
:to="to"
|
|
25
25
|
:text="tertiary"
|
|
26
26
|
:disabled="disabled"
|
|
27
|
+
:href="href"
|
|
28
|
+
:target="target"
|
|
27
29
|
v-on="$listeners"
|
|
28
30
|
>
|
|
29
31
|
<div v-if="hasIcon" class="button__icon">
|
|
@@ -53,6 +55,8 @@ export default class CButton extends Vue {
|
|
|
53
55
|
@Prop({ type: Boolean, default: false }) secondary!: boolean;
|
|
54
56
|
@Prop({ type: Boolean, default: false }) tertiary!: boolean;
|
|
55
57
|
@Prop([Object, String]) to?: RawLocation;
|
|
58
|
+
@Prop(String) href?: string;
|
|
59
|
+
@Prop(String) target?: string;
|
|
56
60
|
|
|
57
61
|
get hasIcon(): boolean {
|
|
58
62
|
return !!(this.icon || this.$slots.icon || this.$scopedSlots.icon);
|
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,92 @@ 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
|
+
}
|
|
72
|
+
|
|
73
|
+
.card__header__title-container {
|
|
74
|
+
flex: 1 1 100%;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.card__header__title {
|
|
78
|
+
.title();
|
|
35
79
|
|
|
36
|
-
|
|
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;
|
|
37
92
|
}
|
|
38
93
|
|
|
39
94
|
.card__body {
|
|
40
|
-
padding:
|
|
95
|
+
padding: @card-body-v-spacing @card-h-spacing;
|
|
41
96
|
|
|
42
|
-
.
|
|
43
|
-
padding-top:
|
|
97
|
+
.card__header:not(.card__header--divided) + & {
|
|
98
|
+
padding-top: @card-body-v-spacing - 4px;
|
|
44
99
|
}
|
|
45
100
|
}
|
|
46
101
|
|
|
47
102
|
.card__body--fluid {
|
|
48
103
|
padding: 0;
|
|
49
104
|
}
|
|
105
|
+
|
|
106
|
+
.card__footer {
|
|
107
|
+
.button-container();
|
|
108
|
+
|
|
109
|
+
&:not(:empty) {
|
|
110
|
+
margin-top: -1 * @card-body-v-spacing;
|
|
111
|
+
padding: 0 @card-h-spacing;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.card__footer--divided {
|
|
116
|
+
&:not(:empty) {
|
|
117
|
+
border-top: @border;
|
|
118
|
+
margin-top: 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
50
121
|
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
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" :fluid="fluid" 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
|
+
/** Removes padding from modal body */
|
|
62
|
+
@Prop({ type: Boolean, default: false }) fluid!: boolean;
|
|
63
|
+
/** Shows loading state + sets loading message if string */
|
|
64
|
+
@Prop([String, Boolean]) loading?: string | boolean;
|
|
65
|
+
/** Allows modal to be dismissed */
|
|
66
|
+
@Prop({ type: Boolean, default: true }) dismissible!: boolean;
|
|
67
|
+
|
|
68
|
+
get hasLoadingMessage(): boolean {
|
|
69
|
+
return typeof this.loading === 'string';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<style lang="less" scoped>
|
|
75
|
+
@import '~@/styles/variables.less';
|
|
76
|
+
|
|
77
|
+
.modal__header__title {
|
|
78
|
+
.subheadline();
|
|
79
|
+
|
|
80
|
+
vertical-align: -4px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.modal__loading {
|
|
84
|
+
text-align: center;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.modal__loading__icon-container {
|
|
88
|
+
padding: 12px 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.modal__loading__icon {
|
|
92
|
+
color: @color-accent-blue;
|
|
93
|
+
font-size: @font-size-2x-large;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.modal__loading__message {
|
|
97
|
+
font-weight: @font-weight-bold;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/deep/ .card__footer:not(:empty) {
|
|
101
|
+
margin-top: -12px;
|
|
102
|
+
padding-bottom: 4px;
|
|
103
|
+
}
|
|
104
|
+
</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>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<fieldset :disabled="disabled">
|
|
2
|
+
<fieldset class="form-field" :disabled="disabled">
|
|
3
3
|
<label
|
|
4
4
|
v-if="label"
|
|
5
5
|
:for="fieldId"
|
|
@@ -29,6 +29,12 @@ export default class FormField extends Vue {
|
|
|
29
29
|
@import '~@/styles/variables.less';
|
|
30
30
|
@import '~@/styles/form-fields.less';
|
|
31
31
|
|
|
32
|
+
.form-field {
|
|
33
|
+
border: none;
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
.form-field__label {
|
|
33
39
|
font-weight: @font-weight-bold;
|
|
34
40
|
margin: 0 0 4px 2px;
|
package/src/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export const CCard: Component;
|
|
|
11
11
|
export const CCheckbox: Component;
|
|
12
12
|
export const CIconButton: Component;
|
|
13
13
|
export const CListItem: Component;
|
|
14
|
+
export const CModal: Component;
|
|
14
15
|
export const CModalLoading: Component;
|
|
15
16
|
export const CSegmentedButton: Component;
|
|
16
17
|
export const CSegmentedButtonOption: Component;
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import _CCard from '@/components/CCard.vue';
|
|
|
7
7
|
import _CCheckbox from '@/components/CCheckbox.vue';
|
|
8
8
|
import _CIconButton from '@/components/CIconButton.vue';
|
|
9
9
|
import _CListItem from '@/components/CListItem.vue';
|
|
10
|
+
import _CModal from '@/components/CModal.vue';
|
|
10
11
|
import _CModalLoading from '@/components/CModalLoading.vue';
|
|
11
12
|
import _CSegmentedButton from '@/components/CSegmentedButton.vue';
|
|
12
13
|
import _CSegmentedButtonOption from '@/components/CSegmentedButtonOption.vue';
|
|
@@ -29,6 +30,7 @@ export const CCard = _CCard;
|
|
|
29
30
|
export const CCheckbox = _CCheckbox;
|
|
30
31
|
export const CIconButton = _CIconButton;
|
|
31
32
|
export const CListItem = _CListItem;
|
|
33
|
+
export const CModal = _CModal;
|
|
32
34
|
export const CModalLoading = _CModalLoading;
|
|
33
35
|
export const CSegmentedButton = _CSegmentedButton;
|
|
34
36
|
export const CSegmentedButtonOption = _CSegmentedButtonOption;
|
|
@@ -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;
|