@bethinkpl/design-system 43.0.0 → 44.0.0

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 (51) hide show
  1. package/dist/design-system.css +1 -1
  2. package/dist/design-system.js +11664 -11121
  3. package/dist/design-system.js.map +1 -1
  4. package/dist/lib/js/components/Form/PinInputField/PinInputField.consts.d.ts +14 -0
  5. package/dist/lib/js/components/Form/PinInputField/PinInputField.types.d.ts +12 -0
  6. package/dist/lib/js/components/Form/PinInputField/PinInputField.utils.d.ts +2 -0
  7. package/dist/lib/js/components/Form/PinInputField/PinInputField.vue.d.ts +91 -0
  8. package/dist/lib/js/components/Form/PinInputField/index.d.ts +5 -0
  9. package/dist/lib/js/components/Form/PinInputField/usePinInputFieldWithinForm.d.ts +8 -0
  10. package/dist/lib/js/components/Form/TextAreaField/TextAreaField.types.d.ts +9 -0
  11. package/dist/lib/js/components/Form/TextAreaField/TextAreaField.vue.d.ts +76 -0
  12. package/dist/lib/js/components/Form/TextAreaField/TextAreaFieldAutosized.vue.d.ts +6 -0
  13. package/dist/lib/js/components/Form/TextAreaField/index.d.ts +4 -0
  14. package/dist/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.vue.d.ts +12 -1
  15. package/dist/lib/js/components/Toast/Toast.consts.d.ts +11 -9
  16. package/dist/lib/js/components/Toast/Toast.vue.d.ts +51 -433
  17. package/dist/lib/js/{components/Form/InputField/useInputFieldWithinForm.d.ts → composables/useTextFieldWithinForm.d.ts} +1 -1
  18. package/dist/lib/js/index.d.ts +4 -0
  19. package/lib/js/components/Form/CheckboxGroupField/CheckboxGroupField.spec.ts +15 -0
  20. package/lib/js/components/Form/CheckboxGroupField/CheckboxGroupField.vue +5 -2
  21. package/lib/js/components/Form/Form.stories.ts +23 -2
  22. package/lib/js/components/Form/InputField/InputField.spec.ts +11 -1
  23. package/lib/js/components/Form/InputField/InputField.vue +7 -4
  24. package/lib/js/components/Form/PinInputField/PinInputField.consts.ts +18 -0
  25. package/lib/js/components/Form/PinInputField/PinInputField.spec.ts +394 -0
  26. package/lib/js/components/Form/PinInputField/PinInputField.stories.ts +124 -0
  27. package/lib/js/components/Form/PinInputField/PinInputField.types.ts +42 -0
  28. package/lib/js/components/Form/PinInputField/PinInputField.utils.ts +5 -0
  29. package/lib/js/components/Form/PinInputField/PinInputField.vue +211 -0
  30. package/lib/js/components/Form/PinInputField/index.ts +6 -0
  31. package/lib/js/components/Form/PinInputField/usePinInputFieldWithinForm.ts +29 -0
  32. package/lib/js/components/Form/SelectField/SelectField.stories.ts +0 -5
  33. package/lib/js/components/Form/SelectField/useSelectFieldWithinForm.ts +1 -1
  34. package/lib/js/components/Form/TextAreaField/TextAreaField.spec.ts +341 -0
  35. package/lib/js/components/Form/TextAreaField/TextAreaField.stories.ts +99 -0
  36. package/lib/js/components/Form/TextAreaField/TextAreaField.types.ts +23 -0
  37. package/lib/js/components/Form/TextAreaField/TextAreaField.vue +190 -0
  38. package/lib/js/components/Form/TextAreaField/TextAreaFieldAutosized.vue +19 -0
  39. package/lib/js/components/Form/TextAreaField/index.ts +5 -0
  40. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.spec.ts +26 -0
  41. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.stories.ts +16 -3
  42. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.vue +5 -0
  43. package/lib/js/components/LoadingBar/LoadingBar.spec.ts +84 -0
  44. package/lib/js/components/LoadingBar/LoadingBar.vue +30 -19
  45. package/lib/js/components/Toast/Toast.consts.ts +4 -2
  46. package/lib/js/components/Toast/Toast.spec.ts +177 -0
  47. package/lib/js/components/Toast/Toast.stories.ts +65 -31
  48. package/lib/js/components/Toast/Toast.vue +176 -195
  49. package/lib/js/{components/Form/InputField/useInputFieldWithinForm.ts → composables/useTextFieldWithinForm.ts} +7 -2
  50. package/lib/js/index.ts +4 -0
  51. package/package.json +1 -1
@@ -1,9 +1,14 @@
1
1
  <template>
2
- <div :style="styles" :class="['ds-toast', toastSize, toastPosition]">
2
+ <div
3
+ :class="['ds-toast', toastSize, toastPosition, toastColor, { '-ds-closable': isClosable }]"
4
+ >
3
5
  <ds-card :loading-bar-color="color" has-loading-bar :loading-bar-time="disappearingTimeout">
4
6
  <template #content>
5
- <div class="ds-toast__content">
6
- <slot name="content" />
7
+ <div class="ds-toast__body">
8
+ <div v-if="title" class="ds-toast__title">{{ title }}</div>
9
+ <div class="ds-toast__content">
10
+ <slot name="content" />
11
+ </div>
7
12
  </div>
8
13
  </template>
9
14
  <template
@@ -14,6 +19,7 @@
14
19
  <ds-button
15
20
  v-if="footerSecondaryButtonText.length"
16
21
  :color="buttonSecondaryColor"
22
+ :size="buttonSize"
17
23
  :icon-right="footerSecondaryButtonIcon"
18
24
  :type="BUTTON_TYPES.OUTLINED"
19
25
  :radius="BUTTON_RADIUSES.ROUNDED"
@@ -23,6 +29,7 @@
23
29
  <ds-button
24
30
  v-if="footerPrimaryButtonText.length"
25
31
  :color="buttonPrimaryColor"
32
+ :size="buttonSize"
26
33
  :icon-right="footerPrimaryButtonIcon"
27
34
  :radius="BUTTON_RADIUSES.ROUNDED"
28
35
  @click="$emit('primary-button-click')"
@@ -31,49 +38,113 @@
31
38
  </div>
32
39
  </template>
33
40
  </ds-card>
41
+ <div v-if="isClosable" class="ds-toast__close">
42
+ <ds-icon-button
43
+ :icon="ICONS.FA_XMARK"
44
+ :size="ICON_BUTTON_SIZES.SMALL"
45
+ :color="ICON_BUTTON_COLORS.NEUTRAL_WEAK"
46
+ @click="close"
47
+ />
48
+ </div>
34
49
  </div>
35
50
  </template>
36
51
 
37
52
  <style scoped lang="scss">
53
+ @import '../../../styles/settings/colors/tokens';
38
54
  @import '../../../styles/settings/spacings';
39
55
  @import '../../../styles/settings/typography/tokens';
40
56
  @import '../../../styles/settings/media-queries';
41
57
 
42
58
  .ds-toast {
43
- position: fixed;
59
+ $root: &;
60
+
61
+ max-width: var(--ds-toast-max-width);
62
+ // anchors the absolutely positioned close button, including under `position: none`
63
+ position: relative;
44
64
  width: 100%;
45
65
 
46
66
  &.-ds-size-small {
47
- max-width: 320px + $space-8 * 2;
48
- padding: $space-8;
67
+ --ds-toast-max-width: 320px;
49
68
  }
50
69
 
51
70
  &.-ds-size-medium {
52
- max-width: 500px + $space-8 * 2;
53
- padding: $space-8;
71
+ --ds-toast-max-width: 500px;
72
+ }
73
+
74
+ &.-ds-size-large {
75
+ --ds-toast-max-width: 700px;
76
+ }
77
+
78
+ // The viewport spacing lives in the offsets rather than in padding, so it applies only to the
79
+ // fixed positions — `none` lets the consumer place (and space) the toast themselves.
80
+ &.-ds-position-left,
81
+ &.-ds-position-center,
82
+ &.-ds-position-right {
83
+ --ds-toast-offset: #{$space-8};
84
+
85
+ bottom: var(--ds-toast-offset);
86
+ max-width: min(var(--ds-toast-max-width), calc(100% - var(--ds-toast-offset) * 2));
87
+ position: fixed;
54
88
 
55
89
  @media (#{breakpoint-s()}) {
56
- max-width: 500px + $space-12 * 2;
57
- padding: $space-12;
90
+ &.-ds-size-medium,
91
+ &.-ds-size-large {
92
+ --ds-toast-offset: #{$space-12};
93
+ }
58
94
  }
59
95
  }
60
96
 
61
97
  &.-ds-position-left {
62
- bottom: 0;
63
- left: 0;
98
+ left: var(--ds-toast-offset);
64
99
  }
65
100
 
66
101
  &.-ds-position-right {
67
- bottom: 0;
68
- right: 0;
102
+ right: var(--ds-toast-offset);
69
103
  }
70
104
 
71
105
  &.-ds-position-center {
72
- bottom: 0;
73
106
  left: 50%;
74
107
  transform: translateX(-50%);
75
108
  }
76
109
 
110
+ &__body {
111
+ display: flex;
112
+ flex-direction: column;
113
+ row-gap: $space-2;
114
+ }
115
+
116
+ &__close {
117
+ position: absolute;
118
+ right: $space-1;
119
+ top: $space-3;
120
+ }
121
+
122
+ &__title {
123
+ @include heading-s-default-bold;
124
+
125
+ color: $color-neutral-text-strong;
126
+
127
+ #{$root}.-ds-size-large & {
128
+ @include heading-m-default-bold;
129
+ }
130
+
131
+ #{$root}.-ds-color-success & {
132
+ color: $color-success-text-strong;
133
+ }
134
+
135
+ #{$root}.-ds-color-warning & {
136
+ color: $color-warning-text-strong;
137
+ }
138
+
139
+ #{$root}.-ds-color-danger & {
140
+ color: $color-danger-text-strong;
141
+ }
142
+
143
+ #{$root}.-ds-color-info & {
144
+ color: $color-info-text-strong;
145
+ }
146
+ }
147
+
77
148
  &__content {
78
149
  @include text-m-default-regular;
79
150
  }
@@ -87,9 +158,15 @@
87
158
  }
88
159
  </style>
89
160
 
90
- <script lang="ts">
91
- import DsButton, { BUTTON_COLORS, BUTTON_RADIUSES, BUTTON_TYPES } from '../Buttons/Button';
161
+ <script lang="ts" setup>
162
+ import DsButton, {
163
+ BUTTON_COLORS,
164
+ BUTTON_RADIUSES,
165
+ BUTTON_SIZES,
166
+ BUTTON_TYPES,
167
+ } from '../Buttons/Button';
92
168
  import DsCard from '../Cards/Card';
169
+ import DsIconButton, { ICON_BUTTON_COLORS, ICON_BUTTON_SIZES } from '../Buttons/IconButton';
93
170
  import {
94
171
  TOAST_COLORS,
95
172
  TOAST_POSITIONS,
@@ -98,184 +175,88 @@ import {
98
175
  ToastPositions,
99
176
  ToastSizes,
100
177
  } from './Toast.consts';
101
- import { defineComponent, PropType, toRaw } from 'vue';
102
- import { ICONS } from '../Icons/Icon';
103
-
104
- function calculateBoundariesOffset(boundariesElement: HTMLElement) {
105
- const borderLeftWidth = window.getComputedStyle(boundariesElement).borderLeftWidth;
106
- const borderRightWidth = window.getComputedStyle(boundariesElement).borderRightWidth;
107
- const boundingClientRect = boundariesElement.getBoundingClientRect();
108
- const boundariesOffsetLeft = boundingClientRect.left + parseInt(borderLeftWidth, 10);
109
- const boundariesOffsetRight =
110
- boundingClientRect.right - parseInt(borderRightWidth, 10) - parseInt(borderLeftWidth, 10);
111
- const boundariesOffsetWidth = boundingClientRect.width;
112
- return {
113
- left: {
114
- left: `${boundariesOffsetLeft}px`,
115
- },
116
- right: {
117
- left: `${boundariesOffsetRight}px`,
118
- transform: 'translateX(-100%)',
119
- },
120
- center: {
121
- left: `${boundariesOffsetLeft + boundariesOffsetWidth / 2}px`,
122
- transform: 'translateX(-50%)',
123
- },
124
- };
125
- }
178
+ import { computed, onMounted, onUnmounted } from 'vue';
179
+ import { ICONS, IconItem } from '../Icons/Icon';
126
180
 
127
- export default defineComponent({
128
- name: 'Toast',
129
- components: {
130
- DsButton,
131
- DsCard,
132
- },
133
- props: {
134
- size: {
135
- type: String as PropType<ToastSizes>,
136
- default: TOAST_SIZES.MEDIUM,
137
- },
138
- position: {
139
- type: String as PropType<ToastPositions>,
140
- default: TOAST_POSITIONS.CENTER,
141
- },
142
- boundariesSelector: {
143
- type: [String, Object] as PropType<string | HTMLElement>,
144
- default: null,
145
- },
146
- color: {
147
- type: String as PropType<ToastColors>,
148
- default: TOAST_COLORS.INFO,
149
- },
150
- footerPrimaryButtonText: {
151
- type: String,
152
- default: '',
153
- },
154
- footerPrimaryButtonIcon: {
155
- type: Object,
156
- default: null,
157
- validator(footerPrimaryButtonIcon: any) {
158
- return (
159
- footerPrimaryButtonIcon == null ||
160
- Object.values(ICONS).includes(toRaw(footerPrimaryButtonIcon))
161
- );
162
- },
163
- },
164
- footerSecondaryButtonText: {
165
- type: String,
166
- default: '',
167
- },
168
- footerSecondaryButtonIcon: {
169
- type: Object,
170
- default: null,
171
- validator(footerSecondaryButtonIcon: any) {
172
- return (
173
- footerSecondaryButtonIcon == null ||
174
- Object.values(ICONS).includes(toRaw(footerSecondaryButtonIcon))
175
- );
176
- },
177
- },
178
- isDisappearing: {
179
- type: Boolean,
180
- default: true,
181
- },
182
- disappearingTimeout: {
183
- type: String,
184
- default: '0',
185
- validator(disappearingTimeout: string) {
186
- return (
187
- (disappearingTimeout === '0' || !isNaN(parseInt(disappearingTimeout, 10))) &&
188
- parseInt(disappearingTimeout, 10) >= 0
189
- );
190
- },
191
- },
192
- },
193
- emits: {
194
- close: () => true,
195
- 'primary-button-click': () => true,
196
- 'secondary-button-click': () => true,
197
- },
198
- data() {
199
- return {
200
- boundariesSelectorElement: null,
201
- boundariesSelectorElementResizeObserver: null,
202
- styles: {},
203
- BUTTON_COLORS: Object.freeze(BUTTON_COLORS),
204
- BUTTON_RADIUSES: Object.freeze(BUTTON_RADIUSES),
205
- BUTTON_TYPES: Object.freeze(BUTTON_TYPES),
206
- TOAST_SIZES: Object.freeze(TOAST_SIZES),
207
- TOAST_POSITIONS: Object.freeze(TOAST_POSITIONS),
208
- };
209
- },
210
- computed: {
211
- buttonPrimaryColor() {
212
- return this.color === TOAST_COLORS.DANGER
213
- ? BUTTON_COLORS.NEUTRAL
214
- : BUTTON_COLORS.PRIMARY;
215
- },
216
- buttonSecondaryColor() {
217
- return this.color === TOAST_COLORS.DANGER
218
- ? BUTTON_COLORS.DANGER
219
- : BUTTON_COLORS.NEUTRAL;
220
- },
221
- toastPosition() {
222
- return `-ds-position-${this.position.toLowerCase()}`;
223
- },
224
- toastSize() {
225
- return `-ds-size-${this.size.toLowerCase()}`;
226
- },
227
- },
228
- mounted() {
229
- this.setBoundariesSelectorElement();
230
- this.calculateStyles();
231
- this.boundariesSelectorElementResizeObserver?.disconnect();
232
- this.boundariesSelectorElementResizeObserver = new ResizeObserver(() => {
233
- this.calculateStyles();
234
- });
235
- if (this.boundariesSelectorElement) {
236
- this.boundariesSelectorElementResizeObserver.observe(this.boundariesSelectorElement);
237
- } else {
238
- //window resize does not work for some reason, so we observe body
239
- this.boundariesSelectorElementResizeObserver.observe(document.querySelector('body'));
240
- }
241
- if (this.isDisappearing && this.disappearingTimeout !== '0') {
242
- setTimeout(
243
- () => this.$emit('close'),
244
- parseInt(this.disappearingTimeout, 10) * 1000 + 100, // 100 ms is to let loading bar animation to finish
245
- );
246
- }
247
- },
248
- beforeUnmount() {
249
- this.boundariesSelectorElementResizeObserver.disconnect();
250
- this.boundariesSelectorElementResizeObserver = null;
251
- },
252
- updated() {
253
- const { right, left } = this.$el.getClientRects()[0];
254
- if (parseInt(left, 10) < 0 || right > document.documentElement.clientWidth) {
255
- this.styles = {
256
- left: '50%',
257
- transform: 'translateX(-50%)',
258
- };
259
- }
260
- },
261
- methods: {
262
- calculateStyles() {
263
- if (this.boundariesSelectorElement) {
264
- this.styles = calculateBoundariesOffset(this.boundariesSelectorElement)[
265
- this.position
266
- ];
267
- } else {
268
- this.styles = {};
269
- }
270
- },
271
- setBoundariesSelectorElement() {
272
- if (typeof this.boundariesSelector === 'string') {
273
- this.boundariesSelectorElement =
274
- document.querySelector(this.boundariesSelector) || null;
275
- } else if (this.boundariesSelector instanceof HTMLElement) {
276
- this.boundariesSelectorElement = this.boundariesSelector;
277
- }
278
- },
279
- },
181
+ const {
182
+ title = '',
183
+ size = TOAST_SIZES.MEDIUM,
184
+ position = TOAST_POSITIONS.CENTER,
185
+ color = TOAST_COLORS.INFO,
186
+ footerPrimaryButtonText = '',
187
+ footerPrimaryButtonIcon = null,
188
+ footerSecondaryButtonText = '',
189
+ footerSecondaryButtonIcon = null,
190
+ isDisappearing = true,
191
+ disappearingTimeout = '0',
192
+ isClosable = false,
193
+ } = defineProps<{
194
+ title?: string;
195
+ size?: ToastSizes;
196
+ position?: ToastPositions;
197
+ color?: ToastColors;
198
+ footerPrimaryButtonText?: string;
199
+ footerPrimaryButtonIcon?: IconItem | null;
200
+ footerSecondaryButtonText?: string;
201
+ footerSecondaryButtonIcon?: IconItem | null;
202
+ isDisappearing?: boolean;
203
+ /** Seconds, as a numeric string. `'0'` disables the auto-close. */
204
+ disappearingTimeout?: string;
205
+ isClosable?: boolean;
206
+ }>();
207
+
208
+ const emit = defineEmits<{
209
+ close: [];
210
+ 'primary-button-click': [];
211
+ 'secondary-button-click': [];
212
+ }>();
213
+
214
+ defineSlots<{
215
+ content?: () => any;
216
+ }>();
217
+
218
+ const buttonPrimaryColor = computed(() =>
219
+ color === TOAST_COLORS.DANGER ? BUTTON_COLORS.NEUTRAL : BUTTON_COLORS.PRIMARY,
220
+ );
221
+
222
+ const buttonSecondaryColor = computed(() =>
223
+ color === TOAST_COLORS.DANGER ? BUTTON_COLORS.DANGER : BUTTON_COLORS.NEUTRAL,
224
+ );
225
+
226
+ const buttonSize = computed(() =>
227
+ size === TOAST_SIZES.SMALL ? BUTTON_SIZES.SMALL : BUTTON_SIZES.MEDIUM,
228
+ );
229
+
230
+ const toastSize = computed(() => `-ds-size-${size.toLowerCase()}`);
231
+
232
+ const toastPosition = computed(() => `-ds-position-${position.toLowerCase()}`);
233
+
234
+ const toastColor = computed(() => `-ds-color-${color}`);
235
+
236
+ let disappearingTimeoutId: ReturnType<typeof setTimeout> | null = null;
237
+
238
+ const clearDisappearingTimeout = () => {
239
+ if (disappearingTimeoutId !== null) {
240
+ clearTimeout(disappearingTimeoutId);
241
+ disappearingTimeoutId = null;
242
+ }
243
+ };
244
+
245
+ // `close` is emitted at most once per toast — dismissing it by hand cancels the pending auto-close
246
+ // so consumers driving a queue (or analytics) off the event never see it twice.
247
+ const close = () => {
248
+ clearDisappearingTimeout();
249
+ emit('close');
250
+ };
251
+
252
+ onMounted(() => {
253
+ if (isDisappearing && disappearingTimeout !== '0') {
254
+ disappearingTimeoutId = setTimeout(
255
+ close,
256
+ parseInt(disappearingTimeout, 10) * 1000 + 100, // 100 ms is to let loading bar animation to finish
257
+ );
258
+ }
280
259
  });
260
+
261
+ onUnmounted(clearDisappearingTimeout);
281
262
  </script>
@@ -1,7 +1,12 @@
1
1
  import { MaybeRefOrGetter, ref, Ref } from 'vue';
2
- import { useFormFieldWithinForm } from '../../../composables/useFormFieldWithinForm';
2
+ import { useFormFieldWithinForm } from './useFormFieldWithinForm';
3
3
 
4
- export function useInputFieldWithinForm(
4
+ /**
5
+ * vee-validate bridge shared by the free-text fields (`InputField`, `TextAreaField`). Validation on
6
+ * `input` only starts once the field has been blurred, so a half-typed value is never marked
7
+ * invalid while the user is still filling it in.
8
+ */
9
+ export function useTextFieldWithinForm(
5
10
  name: MaybeRefOrGetter<string | undefined>,
6
11
  modelValue: Ref<string | undefined>,
7
12
  ) {
package/lib/js/index.ts CHANGED
@@ -53,11 +53,15 @@ export { default as NumberInCircle } from './components/NumberInCircle';
53
53
  export { default as DsNumberInCircle } from './components/NumberInCircle';
54
54
  export * from './components/NumberInCircle/NumberInCircle.consts';
55
55
  export { default as DsPasswordField } from './components/Form/PasswordField';
56
+ export { default as DsPinInputField } from './components/Form/PinInputField';
57
+ export * from './components/Form/PinInputField';
56
58
  export { default as DsSelectField } from './components/Form/SelectField';
57
59
  export * from './components/Form/SelectField';
58
60
  export { default as TabItem } from './components/TabItem';
59
61
  export { default as DsTabItem } from './components/TabItem';
60
62
  export * from './components/TabItem/TabItem.consts';
63
+ export { default as DsTextAreaField } from './components/Form/TextAreaField';
64
+ export * from './components/Form/TextAreaField';
61
65
  export { default as Tile } from './components/Tile';
62
66
  export { default as DsTile } from './components/Tile';
63
67
  export * from './components/Tile/Tile.consts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bethinkpl/design-system",
3
- "version": "43.0.0",
3
+ "version": "44.0.0",
4
4
  "description": "Bethink universe design-system",
5
5
  "repository": {
6
6
  "type": "git",