Reuse the live price stream for the product catalog and physical delivery list.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,861 @@
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="product-picker-root"
|
||||
:class="{
|
||||
'product-picker-root--compact': compact,
|
||||
'product-picker-root--open': isOpen,
|
||||
}"
|
||||
>
|
||||
<label v-if="label" class="product-picker__label">{{ label }}</label>
|
||||
|
||||
<!-- سلکتور جمعوجور -->
|
||||
<div
|
||||
class="product-picker__control"
|
||||
:class="{
|
||||
'product-picker__control--clickable': !disabled && !isEdit,
|
||||
'product-picker__control--disabled': disabled,
|
||||
'product-picker__control--locked': isEdit,
|
||||
'product-picker__control--active': isOpen,
|
||||
}"
|
||||
role="button"
|
||||
:tabindex="disabled ? -1 : 0"
|
||||
:aria-label="label || placeholder"
|
||||
@click="toggleOpen"
|
||||
@keydown.enter.prevent="toggleOpen"
|
||||
>
|
||||
<div class="product-picker__value">
|
||||
<span class="product-picker__icon" aria-hidden="true">
|
||||
<BasicIcon
|
||||
:icon="isEdit ? 'vuesax-linear:lock' : 'vuesax-linear:box-1'"
|
||||
:size="compact ? 16 : 18"
|
||||
:color="selectedItem ? 'var(--App-secondary-color, #e0be7f)' : 'var(--app-text-soft, #9ca3af)'"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="product-picker__text"
|
||||
:class="{ 'product-picker__text--placeholder': !selectedItem && !lockedTitle }"
|
||||
>
|
||||
{{ displayTitle }}
|
||||
</span>
|
||||
<span v-if="displayUnit" class="product-picker__unit">({{ displayUnit }})</span>
|
||||
<span v-if="displaySubtitle" class="product-picker__subtitle">{{ displaySubtitle }}</span>
|
||||
</div>
|
||||
|
||||
<div class="product-picker__trailing">
|
||||
<button
|
||||
v-if="allowClear && selectedItem && !disabled && !isEdit"
|
||||
type="button"
|
||||
class="product-picker__clear-btn"
|
||||
aria-label="پاک کردن"
|
||||
@click.stop="handleClear"
|
||||
>
|
||||
<BasicIcon icon="vuesax-linear:close-circle" size="16" color="var(--app-text-soft, #9ca3af)" />
|
||||
</button>
|
||||
<span class="product-picker__arrow" aria-hidden="true">
|
||||
<BasicIcon
|
||||
v-if="!isEdit"
|
||||
:icon="isOpen ? 'vuesax-linear:arrow-up-1' : 'vuesax-linear:arrow-down-1'"
|
||||
size="16"
|
||||
color="var(--app-text-soft, #9ca3af)"
|
||||
/>
|
||||
<BasicIcon v-else icon="vuesax-linear:lock" size="14" color="var(--app-text-soft, #9ca3af)" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- دراپداون دسکتاپ -->
|
||||
<div
|
||||
v-if="isDesktop && isOpen && !disabled && !isEdit"
|
||||
class="product-picker__dropdown"
|
||||
:style="dropdownStyle"
|
||||
>
|
||||
<div class="product-picker__search">
|
||||
<span class="product-picker__search-icon" aria-hidden="true">
|
||||
<BasicIcon icon="vuesax-linear:search-normal" size="16" color="var(--app-text-soft, #9ca3af)" />
|
||||
</span>
|
||||
<input
|
||||
ref="desktopSearchInputRef"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="product-picker__search-input"
|
||||
placeholder="جستجوی محصول..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
type="button"
|
||||
class="product-picker__clear-query-btn"
|
||||
aria-label="پاک کردن جستجو"
|
||||
@click="searchQuery = ''"
|
||||
>
|
||||
<BasicIcon icon="vuesax-linear:close-circle" size="15" color="var(--app-text-soft, #9ca3af)" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="product-picker__list" role="listbox">
|
||||
<button
|
||||
v-for="p in filteredList"
|
||||
:key="getItemKey(p)"
|
||||
type="button"
|
||||
role="option"
|
||||
class="product-picker__item"
|
||||
:class="{ 'product-picker__item--selected': isSelected(p) }"
|
||||
:aria-selected="isSelected(p)"
|
||||
@click="selectItem(p)"
|
||||
>
|
||||
<div class="product-picker__item-info">
|
||||
<span class="product-picker__item-icon">
|
||||
<BasicIcon
|
||||
icon="vuesax-linear:box-1"
|
||||
size="16"
|
||||
:color="isSelected(p) ? 'var(--App-secondary-color, #e0be7f)' : 'inherit'"
|
||||
/>
|
||||
</span>
|
||||
<span class="product-picker__item-title">{{ getItemTitle(p) }}</span>
|
||||
<span v-if="getItemUnit(p)" class="product-picker__item-unit">({{ getItemUnit(p) }})</span>
|
||||
<span v-if="getItemSubtitle(p)" class="product-picker__item-subtitle">{{ getItemSubtitle(p) }}</span>
|
||||
</div>
|
||||
|
||||
<span v-if="isSelected(p)" class="product-picker__item-check">
|
||||
<BasicIcon icon="vuesax-linear:tick-circle" size="18" color="var(--App-secondary-color, #e0be7f)" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div v-if="loading" class="product-picker__loading">
|
||||
<BasicIcon icon="vuesax-linear:box-time" size="20" class="animate-spin" color="var(--App-secondary-color, #e0be7f)" />
|
||||
<span>در حال دریافت محصولات...</span>
|
||||
</div>
|
||||
<div v-else-if="filteredList.length === 0" class="product-picker__empty">
|
||||
<BasicIcon icon="vuesax-linear:info-circle" size="20" color="var(--app-text-soft, #9ca3af)" />
|
||||
<span>{{ emptyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- اکشنشیت موبایل -->
|
||||
<BasicActionSheet
|
||||
v-if="!isDesktop"
|
||||
v-model:show="isSheetOpen"
|
||||
:title="sheetTitle"
|
||||
:closable="true"
|
||||
@closed="onSheetClosed"
|
||||
>
|
||||
<div class="product-picker-sheet">
|
||||
<div class="product-picker-sheet__search">
|
||||
<span class="product-picker-sheet__search-icon" aria-hidden="true">
|
||||
<BasicIcon icon="vuesax-linear:search-normal" size="18" color="var(--app-text-soft, #9ca3af)" />
|
||||
</span>
|
||||
<input
|
||||
ref="mobileSearchInputRef"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="product-picker-sheet__search-input"
|
||||
placeholder="جستجوی نام یا کد محصول..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
type="button"
|
||||
class="product-picker-sheet__clear-btn"
|
||||
aria-label="پاک کردن جستجو"
|
||||
@click="searchQuery = ''"
|
||||
>
|
||||
<BasicIcon icon="vuesax-linear:close-circle" size="18" color="var(--app-text-soft, #9ca3af)" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="product-picker-sheet__list" role="listbox">
|
||||
<button
|
||||
v-for="p in filteredList"
|
||||
:key="getItemKey(p)"
|
||||
type="button"
|
||||
role="option"
|
||||
class="product-picker-sheet__item"
|
||||
:class="{ 'product-picker-sheet__item--selected': isSelected(p) }"
|
||||
:aria-selected="isSelected(p)"
|
||||
@click="selectItem(p)"
|
||||
>
|
||||
<div class="product-picker-sheet__item-info">
|
||||
<span class="product-picker-sheet__item-icon">
|
||||
<BasicIcon
|
||||
icon="vuesax-linear:box-1"
|
||||
size="18"
|
||||
:color="isSelected(p) ? 'var(--App-secondary-color, #e0be7f)' : 'inherit'"
|
||||
/>
|
||||
</span>
|
||||
<div class="product-picker-sheet__item-texts">
|
||||
<span class="product-picker-sheet__item-title">{{ getItemTitle(p) }}</span>
|
||||
<span
|
||||
v-if="getItemSubtitle(p) || getItemUnit(p)"
|
||||
class="product-picker-sheet__item-meta"
|
||||
>
|
||||
{{ getItemSubtitle(p) || `واحد: ${getItemUnit(p)}` }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span v-if="isSelected(p)" class="product-picker-sheet__item-check">
|
||||
<BasicIcon icon="vuesax-linear:tick-circle" size="20" color="var(--App-secondary-color, #e0be7f)" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div v-if="loading" class="product-picker-sheet__loading">
|
||||
<BasicIcon icon="vuesax-linear:box-time" size="26" class="animate-spin" color="var(--App-secondary-color, #e0be7f)" />
|
||||
<span class="mt-2 text-sm text-gray-400">در حال دریافت محصولات...</span>
|
||||
</div>
|
||||
<div v-else-if="filteredList.length === 0" class="product-picker-sheet__empty">
|
||||
<BasicIcon icon="vuesax-linear:info-circle" size="24" color="var(--app-text-soft, #9ca3af)" />
|
||||
<span>{{ emptyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicActionSheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
|
||||
interface Props {
|
||||
modelValue?: number | string | null
|
||||
value?: number | string | null
|
||||
items?: any[]
|
||||
products?: any[]
|
||||
keyName?: string
|
||||
valueName?: string
|
||||
unitName?: string
|
||||
subtitleName?: string
|
||||
placeholder?: string
|
||||
sheetTitle?: string
|
||||
label?: string
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
isEdit?: boolean
|
||||
lockedTitle?: string
|
||||
allowClear?: boolean
|
||||
compact?: boolean
|
||||
emptyText?: string
|
||||
dropdownStyle?: Record<string, any>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: undefined,
|
||||
value: undefined,
|
||||
items: undefined,
|
||||
products: undefined,
|
||||
keyName: 'id',
|
||||
valueName: 'title',
|
||||
unitName: 'unit',
|
||||
subtitleName: 'subtitle',
|
||||
placeholder: 'انتخاب محصول...',
|
||||
sheetTitle: 'انتخاب محصول',
|
||||
label: '',
|
||||
disabled: false,
|
||||
loading: false,
|
||||
isEdit: false,
|
||||
lockedTitle: '',
|
||||
allowClear: false,
|
||||
compact: false,
|
||||
emptyText: 'محصولی یافت نشد',
|
||||
dropdownStyle: undefined,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [val: any]
|
||||
'update:value': [val: any]
|
||||
'change': [item: any]
|
||||
'clear': []
|
||||
}>()
|
||||
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
const containerRef = ref<HTMLElement>()
|
||||
const desktopSearchInputRef = ref<HTMLInputElement>()
|
||||
const mobileSearchInputRef = ref<HTMLInputElement>()
|
||||
|
||||
const isOpen = ref(false)
|
||||
const isSheetOpen = ref(false)
|
||||
const searchQuery = ref('')
|
||||
|
||||
const effectiveList = computed(() => {
|
||||
if (Array.isArray(props.items)) return props.items
|
||||
if (Array.isArray(props.products)) return props.products
|
||||
return []
|
||||
})
|
||||
|
||||
const currentValue = computed(() => {
|
||||
if (props.modelValue !== undefined) return props.modelValue
|
||||
return props.value
|
||||
})
|
||||
|
||||
function getItemKey(item: any): any {
|
||||
if (!item) return ''
|
||||
return item[props.keyName] !== undefined ? item[props.keyName] : item.id
|
||||
}
|
||||
|
||||
function getItemTitle(item: any): string {
|
||||
if (!item) return ''
|
||||
return String(item[props.valueName] || item.title || item.name || `محصول ${getItemKey(item)}`)
|
||||
}
|
||||
|
||||
function getItemUnit(item: any): string {
|
||||
if (!item) return ''
|
||||
return item[props.unitName] || item.unit || ''
|
||||
}
|
||||
|
||||
function getItemSubtitle(item: any): string {
|
||||
if (!item) return ''
|
||||
return item[props.subtitleName] || item.subtitle || ''
|
||||
}
|
||||
|
||||
const selectedItem = computed(() => {
|
||||
const val = currentValue.value
|
||||
if (val === undefined || val === null || val === '') return null
|
||||
return effectiveList.value.find((p) => String(getItemKey(p)) === String(val)) ?? null
|
||||
})
|
||||
|
||||
const displayTitle = computed(() => {
|
||||
if (props.isEdit && props.lockedTitle) return props.lockedTitle
|
||||
if (selectedItem.value) return getItemTitle(selectedItem.value)
|
||||
const val = currentValue.value
|
||||
if (val !== undefined && val !== null && val !== '') return `محصول ${val}`
|
||||
return props.placeholder
|
||||
})
|
||||
|
||||
const displayUnit = computed(() => {
|
||||
if (props.isEdit) return ''
|
||||
return selectedItem.value ? getItemUnit(selectedItem.value) : ''
|
||||
})
|
||||
|
||||
const displaySubtitle = computed(() => {
|
||||
if (props.isEdit) return ''
|
||||
return selectedItem.value ? getItemSubtitle(selectedItem.value) : ''
|
||||
})
|
||||
|
||||
function isSelected(item: any): boolean {
|
||||
const val = currentValue.value
|
||||
if (val === undefined || val === null || val === '') return false
|
||||
return String(getItemKey(item)) === String(val)
|
||||
}
|
||||
|
||||
function normalizeText(text: string): string {
|
||||
return (text || '')
|
||||
.toLowerCase()
|
||||
.replace(/ي/g, 'ی')
|
||||
.replace(/ك/g, 'ک')
|
||||
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
||||
.trim()
|
||||
}
|
||||
|
||||
const filteredList = computed(() => {
|
||||
const q = normalizeText(searchQuery.value)
|
||||
if (!q) return effectiveList.value
|
||||
return effectiveList.value.filter((p) => {
|
||||
const title = normalizeText(getItemTitle(p))
|
||||
const unit = normalizeText(getItemUnit(p))
|
||||
const subtitle = normalizeText(getItemSubtitle(p))
|
||||
const key = String(getItemKey(p))
|
||||
return title.includes(q) || unit.includes(q) || subtitle.includes(q) || key.includes(q)
|
||||
})
|
||||
})
|
||||
|
||||
function toggleOpen() {
|
||||
if (props.disabled || props.isEdit) return
|
||||
if (isDesktop.value) {
|
||||
isOpen.value = !isOpen.value
|
||||
if (isOpen.value) {
|
||||
nextTick(() => {
|
||||
desktopSearchInputRef.value?.focus()
|
||||
})
|
||||
} else {
|
||||
searchQuery.value = ''
|
||||
}
|
||||
} else {
|
||||
isSheetOpen.value = true
|
||||
nextTick(() => {
|
||||
mobileSearchInputRef.value?.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function selectItem(item: any) {
|
||||
const key = getItemKey(item)
|
||||
emit('update:modelValue', key)
|
||||
emit('update:value', key)
|
||||
emit('change', item)
|
||||
isOpen.value = false
|
||||
isSheetOpen.value = false
|
||||
searchQuery.value = ''
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
emit('update:modelValue', undefined)
|
||||
emit('update:value', undefined)
|
||||
emit('change', undefined)
|
||||
emit('clear')
|
||||
}
|
||||
|
||||
function onSheetClosed() {
|
||||
searchQuery.value = ''
|
||||
}
|
||||
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (containerRef.value && !containerRef.value.contains(e.target as Node)) {
|
||||
isOpen.value = false
|
||||
searchQuery.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.product-picker-root {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
width: 100%;
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
|
||||
&--open {
|
||||
z-index: 50;
|
||||
}
|
||||
--picker-bg: var(--app-surface, var(--admin-bg-surface, #ffffff));
|
||||
--picker-border: var(--app-border, var(--admin-border, #e2e8f0));
|
||||
--picker-border-focus: var(--app-border-strong, var(--App-secondary-color, #e0be7f));
|
||||
--picker-text: var(--app-text-primary, var(--admin-text, #0f172a));
|
||||
--picker-text-soft: var(--app-text-muted, var(--admin-text-secondary, #64748b));
|
||||
--picker-inset: var(--app-surface-inset, rgba(15, 23, 42, 0.03));
|
||||
}
|
||||
|
||||
:global(html[data-theme='dark']) .product-picker-root,
|
||||
:global(html.dark) .product-picker-root {
|
||||
--picker-bg: var(--app-surface, #1e2026);
|
||||
--picker-border: var(--app-border, rgba(255, 255, 255, 0.12));
|
||||
--picker-border-focus: var(--app-border-strong, rgba(255, 255, 255, 0.28));
|
||||
--picker-text: var(--app-text-primary, #ffffff);
|
||||
--picker-text-soft: var(--app-text-muted, rgba(255, 255, 255, 0.55));
|
||||
--picker-inset: var(--app-surface-inset, rgba(255, 255, 255, 0.04));
|
||||
}
|
||||
|
||||
.product-picker__label {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
color: var(--picker-text);
|
||||
}
|
||||
|
||||
.product-picker__control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 2.65rem;
|
||||
padding: 0 0.75rem;
|
||||
border-radius: 0.65rem;
|
||||
border: 1px solid var(--picker-border);
|
||||
background: var(--picker-bg);
|
||||
color: var(--picker-text);
|
||||
direction: rtl;
|
||||
transition: all 0.18s ease;
|
||||
user-select: none;
|
||||
|
||||
.product-picker-root--compact & {
|
||||
min-height: 2.35rem;
|
||||
padding: 0 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border-color: var(--picker-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
&--active {
|
||||
border-color: var(--picker-border-focus);
|
||||
box-shadow: 0 0 0 2px rgba(227, 176, 86, 0.15);
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
background: var(--picker-inset);
|
||||
}
|
||||
|
||||
&--locked {
|
||||
background: var(--picker-inset);
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker__value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.product-picker__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--picker-text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.product-picker-root--compact & {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
&--placeholder {
|
||||
color: var(--picker-text-soft);
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker__unit {
|
||||
font-size: 0.75rem;
|
||||
color: var(--picker-text-soft);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__subtitle {
|
||||
font-size: 0.72rem;
|
||||
color: var(--App-secondary-color, #e0be7f);
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__trailing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__clear-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 9999px;
|
||||
transition: opacity 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker__arrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* دراپداون دسکتاپ */
|
||||
.product-picker__dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1050;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--picker-border);
|
||||
background: var(--picker-bg);
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.12);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.product-picker__search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
height: 2.25rem;
|
||||
padding: 0 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--picker-inset);
|
||||
border: 1px solid var(--picker-border);
|
||||
}
|
||||
|
||||
.product-picker__search-icon {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__search-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: var(--picker-text);
|
||||
font-size: 0.8125rem;
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--picker-text-soft);
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker__clear-query-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.product-picker__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
max-height: 14rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.product-picker__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.65rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
outline: none;
|
||||
text-align: right;
|
||||
|
||||
&:hover {
|
||||
background: var(--picker-inset);
|
||||
border-color: var(--picker-border);
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background: rgba(224, 190, 127, 0.12);
|
||||
border-color: var(--App-secondary-color, #e0be7f);
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker__item-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.product-picker__item-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__item-title {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--picker-text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.product-picker__item-unit {
|
||||
font-size: 0.72rem;
|
||||
color: var(--picker-text-soft);
|
||||
}
|
||||
|
||||
.product-picker__item-subtitle {
|
||||
font-size: 0.72rem;
|
||||
color: var(--App-secondary-color, #e0be7f);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.product-picker__item-check {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker__empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.35rem;
|
||||
padding: 1.25rem 0.5rem;
|
||||
color: var(--picker-text-soft);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
/* شیت موبایل */
|
||||
.product-picker-sheet {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding-bottom: 1.5rem;
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.product-picker-sheet__search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
height: 2.75rem;
|
||||
padding: 0 0.85rem;
|
||||
border-radius: 0.75rem;
|
||||
background: var(--picker-inset);
|
||||
border: 1px solid var(--picker-border);
|
||||
}
|
||||
|
||||
.product-picker-sheet__search-icon {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker-sheet__search-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: var(--picker-text);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--picker-text-soft);
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker-sheet__clear-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.product-picker-sheet__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
max-height: 55vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.product-picker-sheet__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0.65rem 0.75rem;
|
||||
border-radius: 0.65rem;
|
||||
border: 1px solid var(--picker-border);
|
||||
background: var(--picker-bg);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
outline: none;
|
||||
text-align: right;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--App-secondary-color, #e0be7f);
|
||||
}
|
||||
|
||||
&--selected {
|
||||
border-color: var(--App-secondary-color, #e0be7f);
|
||||
background: rgba(224, 190, 127, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.65rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.1rem;
|
||||
height: 2.1rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--picker-inset);
|
||||
flex-shrink: 0;
|
||||
|
||||
.product-picker-sheet__item--selected & {
|
||||
background: var(--App-secondary-color, #e0be7f);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-texts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: var(--picker-text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-meta {
|
||||
font-size: 0.7rem;
|
||||
color: var(--picker-text-soft);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.product-picker-sheet__item-check {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-picker-sheet__empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
padding: 2rem 1rem;
|
||||
color: var(--picker-text-soft);
|
||||
font-size: 0.8125rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user