design-system-next 2.15.6 → 2.16.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.
@@ -64,11 +64,11 @@
64
64
  #sidenav-menu-wrapper,
65
65
  #sidenav-submenu-l1-wrapper,
66
66
  #user-menu-wrapper {
67
- @apply spr-background-color spr-border-color-base spr-overflow-y-auto spr-overflow-x-hidden spr-rounded-[12px] spr-border spr-border-solid spr-drop-shadow-md;
67
+ @apply spr-background-color spr-border-color-base spr-overflow-y-auto spr-overflow-x-hidden spr-rounded-xl spr-border spr-border-solid spr-drop-shadow-md;
68
68
 
69
69
  .v-popper__wrapper {
70
70
  .v-popper__inner {
71
- @apply spr-overflow-hidden spr-rounded-none spr-border-none spr-bg-transparent spr-shadow-drop;
71
+ @apply spr-overflow-hidden spr-rounded-none spr-border-none spr-bg-transparent spr-shadow-none;
72
72
 
73
73
  .slide-fade-enter-active {
74
74
  @apply spr-duration-300 spr-ease-out;
@@ -124,11 +124,11 @@
124
124
 
125
125
  /* #region - Dropdown */
126
126
  #dropdown-wrapper {
127
- @apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
127
+ @apply spr-border-color-weak spr-z-[1000] spr-w-[inherit] spr-overflow-hidden spr-rounded-xl spr-border spr-border-solid spr-shadow-drop;
128
128
 
129
129
  .v-popper__wrapper {
130
130
  .v-popper__inner {
131
- @apply spr-border-color-weak spr-w-full spr-rounded-xl spr-border spr-border-solid spr-font-main spr-shadow-drop;
131
+ @apply spr-w-full spr-rounded-none spr-border-none spr-font-main spr-shadow-none;
132
132
 
133
133
  &::-webkit-scrollbar-track {
134
134
  @apply spr-rounded-br-xl spr-rounded-tr-xl;
@@ -146,11 +146,11 @@
146
146
  #select-wrapper,
147
147
  #multi-select-wrapper,
148
148
  #ladderized-select-wrapper {
149
- @apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
149
+ @apply spr-border-color-weak spr-z-[1000] spr-w-[inherit] spr-overflow-hidden spr-rounded-xl spr-border spr-border-solid spr-shadow-drop;
150
150
 
151
151
  .v-popper__wrapper {
152
152
  .v-popper__inner {
153
- @apply spr-border-color-weak spr-w-full spr-rounded-xl spr-border spr-border-solid spr-font-main spr-shadow-none;
153
+ @apply spr-w-full spr-rounded-none spr-border-none spr-font-main spr-shadow-none;
154
154
 
155
155
  &::-webkit-scrollbar-track {
156
156
  @apply spr-rounded-br-xl spr-rounded-tr-xl;
@@ -183,6 +183,7 @@
183
183
  thead {
184
184
  @apply spr-pr-size-spacing-6xs;
185
185
  }
186
+
186
187
  tbody::-webkit-scrollbar {
187
188
  @apply spr-h-0 spr-w-0; /* Hides the scrollbar in WebKit browsers */
188
189
  }
@@ -418,6 +419,26 @@
418
419
 
419
420
  /* #endregion - Labels */
420
421
 
422
+ /* #region - Attribute Filter */
423
+ #attribute_filter_wrapper {
424
+ @apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
425
+
426
+ .v-popper__wrapper {
427
+ .v-popper__inner {
428
+ @apply spr-border-color-weak spr-w-full spr-rounded-xl spr-border spr-border-solid spr-font-main spr-shadow-none;
429
+
430
+ &::-webkit-scrollbar-track {
431
+ @apply spr-rounded-br-xl spr-rounded-tr-xl;
432
+ }
433
+ }
434
+
435
+ .v-popper__arrow-container {
436
+ @apply spr-hidden;
437
+ }
438
+ }
439
+ }
440
+ /* #endregion - Attribute Filter */
441
+
421
442
  /* #region - Font Size */
422
443
  .spr-font-size-100 {
423
444
  @apply spr-text-100;
@@ -0,0 +1,134 @@
1
+ import type { PropType, ExtractPropTypes } from 'vue';
2
+ import type { MenuListType } from '../list/list';
3
+
4
+ export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
5
+ const TRIGGER_EVENTS = ['click', 'hover', 'focus', 'touch'] as const;
6
+ const POPPER_STRATEGY_TYPES = ['fixed', 'absolute'] as const;
7
+ const PLACEMENTS_TYPES = [
8
+ 'auto',
9
+ 'auto-start',
10
+ 'auto-end',
11
+ 'top',
12
+ 'top-start',
13
+ 'top-end',
14
+ 'right',
15
+ 'right-start',
16
+ 'right-end',
17
+ 'bottom',
18
+ 'bottom-start',
19
+ 'bottom-end',
20
+ 'left',
21
+ 'left-start',
22
+ 'left-end',
23
+ ] as const;
24
+
25
+ export const attributeFilterPropTypes = {
26
+ id: {
27
+ type: String,
28
+ default: 'attribute_filter',
29
+ },
30
+ filterLabel: {
31
+ type: String,
32
+ default: 'Filter',
33
+ },
34
+ headerLabel: {
35
+ type: String,
36
+ default: 'Add Filter',
37
+ },
38
+ triggers: {
39
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
40
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
41
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
42
+ },
43
+ default: () => [],
44
+ },
45
+ popperTriggers: {
46
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
47
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
48
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
49
+ },
50
+ default: () => [],
51
+ },
52
+ autoHide: {
53
+ type: Boolean,
54
+ default: true,
55
+ },
56
+ popperStrategy: {
57
+ type: String,
58
+ validator: (value: 'fixed' | 'absolute') => POPPER_STRATEGY_TYPES.includes(value),
59
+ default: 'absolute',
60
+ },
61
+ distance: {
62
+ type: Number,
63
+ default: 6,
64
+ },
65
+ placement: {
66
+ type: String as PropType<(typeof PLACEMENTS_TYPES)[number]>,
67
+ validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
68
+ default: 'bottom',
69
+ },
70
+ disabled: {
71
+ type: Boolean,
72
+ default: false,
73
+ },
74
+ wrapperPosition: {
75
+ type: String,
76
+ default: 'relative',
77
+ },
78
+ width: {
79
+ type: String,
80
+ default: '100%',
81
+ },
82
+ popperWidth: {
83
+ type: String,
84
+ default: '100%',
85
+ },
86
+ popperInnerWidth: {
87
+ type: String,
88
+ default: 'unset',
89
+ },
90
+ searchable: {
91
+ type: Boolean,
92
+ default: false,
93
+ },
94
+ multiselect: {
95
+ type: Boolean,
96
+ default: false,
97
+ },
98
+ filterMenuList: {
99
+ type: Array as PropType<MenuListType[] | string[]>,
100
+ default: [],
101
+ },
102
+ disableLocalSearch: {
103
+ type: Boolean,
104
+ default: false,
105
+ },
106
+ showBadge: {
107
+ type: Boolean,
108
+ default: true,
109
+ },
110
+ badgeVariant: {
111
+ type: String,
112
+ default: 'danger',
113
+ },
114
+ noList: {
115
+ type: Boolean,
116
+ default: false,
117
+ },
118
+ clearable: {
119
+ type: Boolean,
120
+ default: true,
121
+ }
122
+ };
123
+
124
+ export const attributeFilterEmitTypes = {
125
+ onSaveFilter: (savedFilters: MenuListType[]) => Array.isArray(savedFilters),
126
+ onCloseFilter: () => true,
127
+ onOpenFilter: () => true,
128
+ onSelectFilter: (selectedFilters: MenuListType[]) => Array.isArray(selectedFilters),
129
+ infiniteScrollTrigger: () => true,
130
+ onClearFilter: () => true,
131
+ };
132
+
133
+ export type AttributeFilterPropTypes = ExtractPropTypes<typeof attributeFilterPropTypes>;
134
+ export type AttributeFilterEmitTypes = typeof attributeFilterEmitTypes;
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <Menu
3
+ :shown="isFilterActive"
4
+ aria-id="attribute_filter_wrapper"
5
+ :distance="props.distance"
6
+ :placement="props.placement"
7
+ :triggers="props.triggers"
8
+ :popper-triggers="props.popperTriggers"
9
+ :auto-hide="props.autoHide"
10
+ :disabled="props.disabled"
11
+ :container="`#${props.id}`"
12
+ :strategy="
13
+ props.popperStrategy === 'fixed' || props.popperStrategy === 'absolute' ? props.popperStrategy : 'absolute'
14
+ "
15
+ :delay="0"
16
+ :style="{
17
+ position: props.wrapperPosition,
18
+ width: props.width,
19
+ }"
20
+ @hide="handleClosePopper"
21
+ @show="handleShowPopper"
22
+ >
23
+ <div @click="handleFilterTrigger">
24
+ <slot>
25
+ <spr-chips
26
+ :label="props.filterLabel"
27
+ :active="isFilterActive"
28
+ icon="ph:funnel-simple"
29
+ :badge="props.showBadge && savedFilters.length > 0"
30
+ :badge-text="savedFilters.length.toString()"
31
+ :badge-variant="props.badgeVariant"
32
+ :closable="props.clearable && savedFilters.length > 0"
33
+ :disabled="props.disabled"
34
+ @close="handleClear"
35
+ />
36
+ </slot>
37
+ </div>
38
+
39
+ <div
40
+ :id="props.id"
41
+ :style="{
42
+ width: props.popperWidth,
43
+ }"
44
+ ></div>
45
+ <template #popper="{ hide }">
46
+ <div
47
+ id="attribute_filter_popper"
48
+ :style="{
49
+ width: props.popperInnerWidth,
50
+ }"
51
+ >
52
+ <slot name="header">
53
+ <div
54
+ id="attribute_filter_header"
55
+ class="spr-border-color-weak spr-flex spr-items-center spr-justify-between spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
56
+ >
57
+ <span class="spr-text-color-strong spr-text-300 spr-font-medium"> {{ props.headerLabel }} </span>
58
+ <Icon
59
+ icon="ph:x"
60
+ width="20px"
61
+ height="20px"
62
+ class="spr-text-color-weak spr-cursor-pointer"
63
+ @click="hide()"
64
+ />
65
+ </div>
66
+ </slot>
67
+ <div
68
+ v-if="props.searchable"
69
+ id="attribute_filter_subheader"
70
+ class="spr-border-color-weak spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
71
+ >
72
+ <spr-input-search
73
+ v-model="searchModel"
74
+ label=""
75
+ placeholder="Search..."
76
+ class="!spr-py-0"
77
+ @focus="isSearchFocused = true"
78
+ @blur="isSearchFocused = false"
79
+ />
80
+ </div>
81
+ <slot name="actions"> </slot>
82
+ <slot name="body">
83
+ <div
84
+ v-if="!noList"
85
+ ref="filterDropdownRef"
86
+ id="attribute_filter_body"
87
+ class="spr-max-h-[250px] spr-overflow-y-auto"
88
+ >
89
+ <spr-list
90
+ v-model="selectedFilters"
91
+ :menu-list="attributeFilterList"
92
+ :multi-select="props.multiselect"
93
+ @update:model-value="handleOnSelect"
94
+ />
95
+ </div>
96
+ </slot>
97
+ <slot name="footer">
98
+ <div
99
+ id="attribute_filter_footer"
100
+ class="spr-border-color-weak spr-flex spr-items-center spr-justify-end spr-gap-size-spacing-3xs spr-border-x-0 spr-border-b-0 spr-border-t spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
101
+ >
102
+ <spr-button variant="secondary" size="medium" @click="isFilterActive = false"> Cancel </spr-button>
103
+ <spr-button variant="primary" size="medium" tone="success" @click="handleSave"> Save </spr-button>
104
+ </div>
105
+ </slot>
106
+ </div>
107
+ </template>
108
+ </Menu>
109
+ </template>
110
+ <script lang="ts" setup>
111
+ import { Menu } from 'floating-vue';
112
+ import { attributeFilterEmitTypes, attributeFilterPropTypes } from './attribute-filter';
113
+ import { useAttributeFilter } from './use-attribute-filter';
114
+ import SprChips from '../chips/chips.vue';
115
+ import { Icon } from '@iconify/vue';
116
+ import SprInputSearch from '../input/input-search/input-search.vue';
117
+ import SprList from '../list/list.vue';
118
+ import SprButton from '../button/button.vue';
119
+
120
+ import 'floating-vue/dist/style.css';
121
+
122
+ const props = defineProps(attributeFilterPropTypes);
123
+ const emit = defineEmits(attributeFilterEmitTypes);
124
+ const searchModel = defineModel<string>('search', {
125
+ default: '',
126
+ });
127
+ const {
128
+ isFilterActive,
129
+ isSearchFocused,
130
+ attributeFilterList,
131
+ handleFilterTrigger,
132
+ selectedFilters,
133
+ savedFilters,
134
+ handleClosePopper,
135
+ handleShowPopper,
136
+ handleSave,
137
+ handleOnSelect,
138
+ filterDropdownRef,
139
+ handleClear,
140
+ } = useAttributeFilter(props, emit, searchModel);
141
+ </script>
@@ -0,0 +1,125 @@
1
+ import type { MenuListType } from '../list/list';
2
+ import type { AttributeFilterPropTypes, AttributeFilterEmitTypes } from './attribute-filter';
3
+ import { onMounted, ref, toRefs, watch, type SetupContext, type Ref } from 'vue';
4
+ import { useInfiniteScroll } from '@vueuse/core';
5
+ export const useAttributeFilter = (
6
+ props: AttributeFilterPropTypes,
7
+ emit: SetupContext<AttributeFilterEmitTypes>['emit'],
8
+ searchModel: Ref<string>,
9
+ ) => {
10
+ const isFilterActive = ref(false);
11
+ const isSearchFocused = ref(false);
12
+ const selectedFilters = ref<MenuListType[]>([]);
13
+ const savedFilters = ref<MenuListType[]>([]);
14
+ const isSaving = ref(false);
15
+ const filterDropdownRef = ref<HTMLDivElement | null>(null);
16
+
17
+ const { filterMenuList, disableLocalSearch, noList } = toRefs(props);
18
+ const attributeFilterList = ref<MenuListType[]>([]);
19
+
20
+ const handleFilterTrigger = () => {
21
+ isFilterActive.value = !isFilterActive.value;
22
+ };
23
+
24
+ const getFilterList = () => {
25
+ if (Array.isArray(filterMenuList.value)) {
26
+ attributeFilterList.value = filterMenuList.value as MenuListType[];
27
+ }
28
+
29
+ if (typeof filterMenuList.value[0] === 'string' || typeof attributeFilterList.value[0] === 'number') {
30
+ attributeFilterList.value = (filterMenuList.value as string[] | number[]).map((item) => ({
31
+ text: item.toString(),
32
+ value: item,
33
+ }));
34
+ }
35
+ };
36
+
37
+ const handleClosePopper = () => {
38
+ if (isSaving.value) {
39
+ isSaving.value = false;
40
+ return;
41
+ }
42
+
43
+ isFilterActive.value = false;
44
+ emit('onCloseFilter');
45
+ };
46
+
47
+ const handleShowPopper = () => {
48
+ emit('onOpenFilter');
49
+ if (noList.value) return;
50
+
51
+ selectedFilters.value = [...savedFilters.value];
52
+ };
53
+
54
+ const handleSave = () => {
55
+ isSaving.value = true;
56
+ savedFilters.value = [...selectedFilters.value];
57
+ isFilterActive.value = false;
58
+
59
+ emit('onSaveFilter', savedFilters.value);
60
+ };
61
+
62
+ const getFilteredMenuList = (list: MenuListType[]) => {
63
+ return list.filter((item: MenuListType) => {
64
+ const searchTerm = searchModel.value.toLowerCase().trim();
65
+ return item.text.toLowerCase().trim().includes(searchTerm);
66
+ });
67
+ };
68
+
69
+ const handleSearch = () => {
70
+ if (disableLocalSearch.value || noList.value) return;
71
+ getFilterList();
72
+ attributeFilterList.value = getFilteredMenuList(attributeFilterList.value);
73
+ };
74
+
75
+ const handleOnSelect = (selectedFilters: MenuListType[]) => {
76
+ emit('onSelectFilter', selectedFilters);
77
+ };
78
+
79
+ const handleClear = () => {
80
+ selectedFilters.value = [];
81
+ savedFilters.value = [];
82
+ emit('onClearFilter');
83
+ };
84
+
85
+ useInfiniteScroll(
86
+ filterDropdownRef,
87
+ () => {
88
+ if (!filterDropdownRef.value) return;
89
+ const element = filterDropdownRef.value;
90
+ const hasVerticalScrollbar = element.scrollHeight > element.clientHeight;
91
+
92
+ if (hasVerticalScrollbar) {
93
+ emit('infiniteScrollTrigger');
94
+ }
95
+ },
96
+ { distance: 10 },
97
+ );
98
+
99
+ onMounted(() => {
100
+ if (noList.value) return;
101
+ getFilterList();
102
+ });
103
+
104
+ watch(filterMenuList, () => {
105
+ if (noList.value) return;
106
+ getFilterList();
107
+ });
108
+
109
+ watch(searchModel, () => handleSearch());
110
+
111
+ return {
112
+ isFilterActive,
113
+ isSearchFocused,
114
+ attributeFilterList,
115
+ handleFilterTrigger,
116
+ selectedFilters,
117
+ handleClosePopper,
118
+ handleShowPopper,
119
+ handleSave,
120
+ savedFilters,
121
+ handleOnSelect,
122
+ filterDropdownRef,
123
+ handleClear,
124
+ };
125
+ };
@@ -2,9 +2,17 @@ import type { PropType, ExtractPropTypes } from 'vue';
2
2
 
3
3
  export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
4
4
 
5
+ const TONES = ['plain', 'neutral', 'success', 'information', 'pending', 'caution', 'accent', 'danger'] as const;
5
6
  const BORDER_RADIUS_SIZE = ['xl', 'lg', 'md', 'sm', 'xs', '2xs'] as const;
6
7
 
7
8
  export const cardPropTypes = {
9
+ id: {
10
+ type: String,
11
+ },
12
+ tone: {
13
+ type: String as PropType<(typeof TONES)[number] | undefined>,
14
+ validator: (val: string | undefined) => !val || TONES.includes(val as (typeof TONES)[number]),
15
+ },
8
16
  title: {
9
17
  type: String,
10
18
  default: '',
@@ -30,6 +38,10 @@ export const cardPropTypes = {
30
38
  validator: (value: (typeof BORDER_RADIUS_SIZE)[number]) => BORDER_RADIUS_SIZE.includes(value),
31
39
  default: 'xl',
32
40
  },
41
+ borderWidth: {
42
+ type: String,
43
+ default: '1px',
44
+ },
33
45
  hasCollapsible: {
34
46
  type: Boolean,
35
47
  default: false,
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="cardClasses.baseClasses">
2
+ <div :class="cardClasses.baseClasses" :style="{ borderWidth: props.borderWidth }">
3
3
  <div v-if="props.showHeader && ($slots.header || props.title)" :class="cardClasses.headerClasses">
4
4
  <div v-if="props.title" class="spr-flex spr-items-center">
5
5
  <Icon
@@ -12,21 +12,42 @@ interface CardClasses {
12
12
  }
13
13
 
14
14
  export const useCard = (props: CardPropTypes, slots: Slots) => {
15
- const { title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding, flexbox } =
15
+ const { tone, title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding, flexbox } =
16
16
  toRefs(props);
17
17
 
18
18
  const cardClasses: ComputedRef<CardClasses> = computed(() => {
19
- const baseClasses = classNames(
20
- `spr-bg-white-50 spr-text-color-strong spr-border spr-border-solid spr-border-mushroom-200`,
21
- {
22
- 'spr-rounded-border-radius-2xs': borderRadiusSize.value === 'xs',
23
- 'spr-rounded-border-radius-xs': borderRadiusSize.value === 'sm',
24
- 'spr-rounded-border-radius-sm': borderRadiusSize.value === 'md',
25
- 'spr-rounded-border-radius-md': borderRadiusSize.value === 'xs',
26
- 'spr-rounded-border-radius-lg': borderRadiusSize.value === 'lg',
27
- 'spr-rounded-border-radius-xl': borderRadiusSize.value === 'xl' || !borderRadiusSize.value,
28
- },
29
- );
19
+ const toneValue = tone?.value;
20
+
21
+ const baseClasses = classNames(`spr-border-solid`, {
22
+ // Tones
23
+ 'spr-background-color-base': !toneValue,
24
+ 'spr-bg-white': toneValue === 'plain',
25
+ 'spr-background-color-surface': toneValue === 'neutral',
26
+ 'spr-background-color-success-weak': toneValue === 'success',
27
+ 'spr-background-color-information-weak': toneValue === 'information',
28
+ 'spr-background-color-pending-weak': toneValue === 'pending',
29
+ 'spr-background-color-caution-weak': toneValue === 'caution',
30
+ 'spr-background-color-accent-weak': toneValue === 'accent',
31
+ 'spr-background-color-danger-weak': toneValue === 'danger',
32
+
33
+ // Borders
34
+ 'spr-border-color-weak': !toneValue || toneValue === 'plain',
35
+ 'spr-border-color-base': toneValue === 'neutral',
36
+ 'spr-border-color-success-base': toneValue === 'success',
37
+ 'spr-border-color-information-base': toneValue === 'information',
38
+ 'spr-border-color-pending-base': toneValue === 'pending',
39
+ 'spr-border-color-caution-base': toneValue === 'caution',
40
+ 'spr-border-color-accent-base': toneValue === 'accent',
41
+ 'spr-border-color-danger-base': toneValue === 'danger',
42
+
43
+ // Border radius
44
+ 'spr-rounded-border-radius-2xs': borderRadiusSize.value === 'xs',
45
+ 'spr-rounded-border-radius-xs': borderRadiusSize.value === 'sm',
46
+ 'spr-rounded-border-radius-sm': borderRadiusSize.value === 'md',
47
+ 'spr-rounded-border-radius-md': borderRadiusSize.value === 'xs',
48
+ 'spr-rounded-border-radius-lg': borderRadiusSize.value === 'lg',
49
+ 'spr-rounded-border-radius-xl': borderRadiusSize.value === 'xl' || !borderRadiusSize.value,
50
+ });
30
51
 
31
52
  const headerClasses = classNames(`spr-flex spr-items-center transition-all duration-300 ease-in-out`, {
32
53
  'spr-min-h-[18px]': slots.header,
@@ -39,8 +39,7 @@ export const dropdownPropTypes = {
39
39
  },
40
40
  menuList: {
41
41
  type: Array as PropType<MenuListType[] | string[] | Record<string, unknown>[]>,
42
- required: true,
43
- default: [],
42
+ default: () => [],
44
43
  },
45
44
  searchableMenu: {
46
45
  type: Boolean,
@@ -116,10 +115,6 @@ export const dropdownPropTypes = {
116
115
  type: Boolean,
117
116
  default: false,
118
117
  },
119
- dropdown: {
120
- type: Boolean,
121
- default: false,
122
- },
123
118
  lozenge: {
124
119
  type: Boolean,
125
120
  default: false,