Skip to content

Commit

Permalink
feat: [Select] - related #384 - global refacto (style wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgoud committed Oct 15, 2024
1 parent 35d50b6 commit 1db2e5b
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 37 deletions.
5 changes: 3 additions & 2 deletions packages/components/select/src/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import '@prestashopcorp/puik-components/option/style/css';
import type Option from './option.vue';

export interface OptionType {
label: string
value: any
[key: string]: any
}
export interface OptionProps {
option: OptionType
labelKey: string
valueKey: string
selectedOptions: OptionType[]
multiSelect?: boolean
}
Expand Down
31 changes: 22 additions & 9 deletions packages/components/select/src/option.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
<template>
<div class="puik-option">
<PuikCheckbox
v-if="props.multiSelect"
v-model="isSelected"
:label="option.label"
@change="selectOption"
<div
:class="[
'puik-option',
{ 'puik-option-single--selected' : isSelected && !props.multiSelect}
]"
>
<template v-if="props.multiSelect">
<PuikCheckbox
v-model="isSelected"
:sr-label="option[props.labelKey]"
@change="selectOption"
/>
<label>{{ option[props.labelKey] }}</label>
</template>
<label v-else>
{{ option[props.labelKey] }}
</label>
<PuikIcon
v-if="isSelected && !props.multiSelect"
class="puik-option__selected-icon"
icon="check"
/>
<div v-else>
{{ option.label }}
</div>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { PuikCheckbox } from '../../checkbox';
import { PuikIcon } from '../../icon';
import type { OptionProps, OptionEmits } from './option';
defineOptions({
Expand Down
5 changes: 4 additions & 1 deletion packages/components/select/src/select.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import '@prestashopcorp/puik-components/select/style/css';
import type Select from './select.vue';
import type { OptionType } from './option';

export interface SelectProps {
options: OptionType[]
labelKey: string
valueKey: string
modelValue: any
multiSelect?: boolean
searchable?: boolean
disabled?: boolean
error?: string
}

export type SelectInstance = InstanceType<typeof Select>;
59 changes: 44 additions & 15 deletions packages/components/select/src/select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,37 @@
<div
v-if="selectedOptions.length > 0 "
class="puik-select__chip-container"
tabindex="0"
@click.stop="toggleOptions"
>
<PuikIcon
class="puik-select__drop-icon"
icon="unfold_more"
/>
<puik-chip
v-for="option in selectedOptions"
:id="`chip-${option.label}`"
:key="option.value"
:content="option.label"
:id="`chip-${option[props.labelKey]}`"
:key="option[props.valueKey]"
:content="option[props.labelKey]"
size="small"
@close="deselectOption(option)"
@click.stop="openOptions"
/>
</div>
<PuikInput
<puik-input
v-else
class="puik-select__multiple-input"
type="text"
placeholder="Select an option"
readonly
@click.stop="toggleOptions"
/>
>
<template #append>
<PuikIcon icon="unfold_more" />
</template>
</puik-input>
</template>
<PuikInput
<puikInput
v-else
v-model="selectedSingleOption"
type="text"
Expand All @@ -38,24 +49,31 @@
v-if="showOptions"
class="puik-select__options-container"
>
<PuikInput
<puik-input
v-if="searchable"
v-model="searchQuery"
class="puik-select__search-input"
type="text"
placeholder="Search..."
@input="searchOptions"
/>
>
<template #prepend>
<PuikIcon icon="search" />
</template>
</puik-input>
<PuikCheckbox
v-if="multiSelect"
v-model="isAllSelected"
class="puik-select__select-all"
:label="isAllSelected ? 'Deselect All' : 'Select All'"
:indeterminate="selectAllIndeterminate"
@change="toggleSelectAll"
/>
<puik-option
v-for="option in filteredOptions"
:key="option.value"
:key="option[props.valueKey]"
:label-key="labelKey"
:value-key="valueKey"
:selected-options="selectedOptions"
:option="option"
:multi-select="props.multiSelect"
Expand All @@ -68,7 +86,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { vOnClickOutside } from '@vueuse/components';
import { PuikCheckbox, PuikChip, PuikInput, PuikOption } from '@prestashopcorp/puik-components';
import { PuikCheckbox, PuikChip, PuikIcon, PuikInput, PuikOption } from '@prestashopcorp/puik-components';
import type { OptionType } from './option';
import type { SelectProps } from './select';
Expand All @@ -85,6 +103,7 @@ const emit = defineEmits(['update:modelValue', 'search']);
const selectedOptions = ref<any[]>([]);
const showOptions = ref(false);
const searchQuery = ref('');
const selectAllIndeterminate = ref(false);
const selectedSingleOption = computed(() => {
return selectedOptions.value.length > 0 ? selectedOptions.value[0].label : '';
Expand All @@ -95,10 +114,14 @@ const filteredOptions = computed(() => {
const isAllSelected = computed(() => {
return selectedOptions.value.length === props.options.length;
});
const updateSelectAllIndeterminate = () => {
selectAllIndeterminate.value = selectedOptions.value.length > 0 && selectedOptions.value.length < props.options.length;
};
const toggleOptions = () => {
showOptions.value = !showOptions.value;
console.log(showOptions.value);
};
const openOptions = () => {
showOptions.value = true;
};
const closeOptions = () => {
showOptions.value = false;
Expand All @@ -110,28 +133,34 @@ const selectOption = (option: any) => {
} else {
selectedOptions.value.push(option);
}
updateSelectAllIndeterminate();
} else {
selectedOptions.value = [option];
showOptions.value = false;
}
emit('update:modelValue', selectedOptions.value);
};
const deselectOption = (option: OptionType) => {
selectedOptions.value = selectedOptions.value.filter(opt => opt !== option);
updateSelectAllIndeterminate();
emit('update:modelValue', selectedOptions.value);
};
const toggleSelectAll = () => {
if (isAllSelected.value) {
selectedOptions.value = [];
} else {
selectedOptions.value = [...props.options];
}
updateSelectAllIndeterminate();
emit('update:modelValue', selectedOptions.value);
};
const searchOptions = () => {
emit('search', searchQuery.value);
};
const deselectOption = (option: OptionType) => {
selectedOptions.value = selectedOptions.value.filter(opt => opt !== option);
};
</script>

<style lang="scss">
@use '@prestashopcorp/puik-theme/src/base.scss';
@use '@prestashopcorp/puik-theme/src/puik-select.scss';
@use '@prestashopcorp/puik-theme/src/puik-option.scss';
</style>
2 changes: 1 addition & 1 deletion packages/theme/src/puik-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@apply border-primary;
}
&--focus {
@apply border-primary ring ring-blue;
@apply border-primary ring ring-inset ring-blue;
}
&--success {
@apply border-green bg-green-50;
Expand Down
10 changes: 7 additions & 3 deletions packages/theme/src/puik-option.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.puik-option {
@apply flex place-content-between relative cursor-default select-none p-2 text-primary-800 transition-colors;
&--selected {
@apply px-3 py-2 flex justify-between space-x-2 relative cursor-default select-none text-primary-800 transition-colors;
&-single--selected {
@apply bg-primary-300;
}
&:hover:not(&--disabled, &--selected) {
Expand All @@ -19,6 +19,10 @@
@apply font-primary font-normal block truncate;
}
&__selected-icon {
@apply font-materialIcons inset-y-0 right-0 flex items-center w-5 ml-2;
@apply ml-auto;
}
}

.puik-option.puik-option-single--selected {
justify-content: space-between;
}
29 changes: 23 additions & 6 deletions packages/theme/src/puik-select.scss
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
@use './common/typography.scss';

.puik-select {
@apply relative;
@extend .puik-body-default;
&__drop-icon {
@apply absolute cursor-pointer right-3 top-[50%] -translate-y-1/2;
}
&__multiple-input {
@apply min-h-[42px] cursor-default;
@apply cursor-default;
}
&__chip-container {
@apply p-2 flex space-x-1 ring-1 ring-black ring-opacity-5
focus-visible:outline-none sm:text-sm overflow-x-hidden;
@apply px-3 py-2 pr-10 flex border border-primary-400 font-primary transition-colors text-primary space-x-1 overflow-x-hidden;
&:hover {
@apply border-primary;
}
&--focus {
@apply border-primary ring ring-inset ring-blue;
}
&--disabled {
@apply bg-white hover:border-primary-400 cursor-not-allowed;
}
}
&__options-container {
@apply absolute w-full bg-white text-base shadow-lg ring-1 ring-black ring-opacity-5
@apply absolute top-[100%] w-full bg-white text-base z-10 shadow-lg ring-1 ring-black ring-opacity-5
focus-visible:outline-none sm:text-sm overflow-x-hidden;
top: calc(100% + 3px);
}
&__select-all {
@apply flex relative cursor-default select-none p-2 text-primary-800 transition-colors;
@apply px-3 py-2 flex relative select-none text-primary-800 transition-colors;
label {
@apply grow cursor-default;
}
&--selected {
@apply bg-primary-300;
}
Expand All @@ -35,6 +49,9 @@
@apply font-materialIcons inset-y-0 right-0 flex items-center w-5 ml-2;
}
}
&__search-input {
@apply px-3 py-2;
}
label {
@apply font-primary font-normal block truncate;
}
Expand Down

0 comments on commit 1db2e5b

Please sign in to comment.