UI update
This commit is contained in:
@ -1,10 +1,38 @@
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions a11y_label_has_associated_control slot_element_deprecated svelte_self_deprecated -->
|
||||
<!-- svelte-ignore a11y_click_1vents_have_key_events a11y_no_static_element_interactions a11y_label_has_associated_control slot_element_deprecated svelte_self_deprecated -->
|
||||
<!-- lib/pages/print/PrintUI.svelte -->
|
||||
<script>
|
||||
import { QUALITIES, COLOR_MODES, FORMATS } from '$lib/common/utils/pricing.util.js';
|
||||
import { QUALITIES, COLOR_MODES } from '$lib/common/utils/pricing.util.js';
|
||||
import SliderOption from './SliderOption.svelte';
|
||||
import ToggleOption from './ToggleOption.svelte';
|
||||
import GearOption from './GearOption.svelte';
|
||||
import FormatPicker from './FormatPicker.svelte';
|
||||
|
||||
/** @type {'button' | 'icon-button' | 'counter' | 'toggle-group' | 'range-slider' | 'file-card' | 'upload-zone' | 'footer'} */
|
||||
const QUALITY_LABELS = QUALITIES.map((q) => q.label);
|
||||
const COLOR_LABELS = COLOR_MODES.map((c) => c.label);
|
||||
const DPI_OPTIONS = ['300', '600', 'Авто', '1200', '2400'];
|
||||
const SIDES_OPTIONS = ['Односторонняя', 'Двусторонняя'];
|
||||
const FORMAT_OPTIONS = ['Авто', 'A2', 'A3', 'A4', 'A5', '10x15'];
|
||||
|
||||
/** @type {'button' | 'icon-button' | 'counter' | 'toggle-group' | 'file-card' | 'upload-zone' | 'footer'} */
|
||||
let { as, ...props } = $props();
|
||||
|
||||
// локально на экземпляр карточки файла
|
||||
let formatOpen = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
if (!props.file?.expanded) formatOpen = false;
|
||||
});
|
||||
|
||||
function pagesText(file) {
|
||||
const t = props.pagesLabel?.(file);
|
||||
if (!t || t === 'Все') return 'Все страницы';
|
||||
return t;
|
||||
}
|
||||
|
||||
function formatText(file) {
|
||||
if (!file.format || file.format === 'Авто') return `Авто (${file.autoFormat ?? 'A4'})`;
|
||||
return file.format;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if as === 'button'}
|
||||
@ -50,24 +78,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else if as === 'range-slider'}
|
||||
<div class="option-sub quality-sub">
|
||||
<label class="option-title">{props.label} – {QUALITIES[props.value]?.label ?? ''}</label>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={props.options.length - 1}
|
||||
step={1}
|
||||
value={props.value}
|
||||
oninput={(e) => props.onChange?.(parseInt(e.target.value))}
|
||||
class="quality-slider"
|
||||
style="--fill: {Math.round((props.value / (props.options.length - 1)) * 100)}%"
|
||||
/>
|
||||
<div class="quality-marks-inline">
|
||||
{#each props.options as q}<span class="quality-mark">{q.label}</span>{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else if as === 'file-card'}
|
||||
{@const file = props.file}
|
||||
<section class="file-card" class:expanded={file.expanded}>
|
||||
@ -83,28 +93,66 @@
|
||||
</div>
|
||||
{#if file.expanded}
|
||||
<div class="file-options">
|
||||
<div class="print-params-row">
|
||||
<div class="pages-control">
|
||||
<label class="option-title compact">Страницы:</label>
|
||||
<button class="pages-btn" onclick={() => props.onPageSelect?.(file)}>
|
||||
{props.pagesLabel?.(file) ?? '...'}
|
||||
</button>
|
||||
<!-- 1. Кнопки с настройками -->
|
||||
<!-- 2. Переключатели — сразу под кнопками настроек -->
|
||||
<div class="duo-group">
|
||||
<div class="duo-row">
|
||||
<GearOption value={pagesText(file)} onclick={() => props.onPageSelect?.(file)} />
|
||||
<GearOption value={formatText(file)} open={formatOpen} onclick={() => (formatOpen = !formatOpen)} />
|
||||
</div>
|
||||
<div class="copies-control-inline">
|
||||
<label class="option-title compact">Кол-во:</label>
|
||||
<svelte:self
|
||||
as="counter"
|
||||
value={file.copies}
|
||||
min={1}
|
||||
onIncrement={() => props.onUpdate?.(file.id, { copies: file.copies + 1 })}
|
||||
onDecrement={() => props.onUpdate?.(file.id, { copies: Math.max(1, file.copies - 1) })}
|
||||
<div class="duo-row">
|
||||
<ToggleOption
|
||||
label="Печать"
|
||||
options={SIDES_OPTIONS}
|
||||
value={file.sides ?? 'Односторонняя'}
|
||||
onchange={(v) => props.onUpdate?.(file.id, { sides: v })}
|
||||
/>
|
||||
<ToggleOption
|
||||
label="Цветность"
|
||||
options={COLOR_LABELS}
|
||||
value={COLOR_LABELS[Math.max(0, COLOR_MODES.findIndex((c) => c.id === file.colorMode))]}
|
||||
onchange={(v, i) => props.onUpdate?.(file.id, { colorMode: COLOR_MODES[i]?.id })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:self as="toggle-group" label="Формат" options={FORMATS} selected={file.format} onSelect={(v) => props.onUpdate?.(file.id, { format: v })} />
|
||||
<svelte:self as="toggle-group" label="Цветность" options={COLOR_MODES} selected={file.colorMode} valueKey="id" labelKey="label" onSelect={(v) => props.onUpdate?.(file.id, { colorMode: v })} />
|
||||
<svelte:self as="range-slider" label="Качество" options={QUALITIES} value={file.qualityIndex} onChange={(v) => props.onUpdate?.(file.id, { qualityIndex: v })} />
|
||||
|
||||
<!-- 3. Ползунки — ниже -->
|
||||
<div class="slider-options">
|
||||
<SliderOption
|
||||
label="Качество"
|
||||
options={QUALITY_LABELS}
|
||||
hint="Качество печати: влияет на расход тонера и время прогрева."
|
||||
value={QUALITIES[file.qualityIndex]?.label ?? QUALITY_LABELS[1] ?? QUALITY_LABELS[0]}
|
||||
onchange={(v) => props.onUpdate?.(file.id, { qualityIndex: QUALITY_LABELS.indexOf(v) })}
|
||||
/>
|
||||
<SliderOption
|
||||
label="DPI"
|
||||
options={DPI_OPTIONS}
|
||||
hint="Разрешение печати. «Авто» подбирает DPI по содержимому страницы."
|
||||
value={file.dpi ?? 'Авто'}
|
||||
onchange={(v) => props.onUpdate?.(file.id, { dpi: v })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 4. Количество — в конце -->
|
||||
<div class="copies-row">
|
||||
<label class="option-title compact">Кол-во:</label>
|
||||
<svelte:self
|
||||
as="counter"
|
||||
value={file.copies}
|
||||
min={1}
|
||||
onIncrement={() => props.onUpdate?.(file.id, { copies: file.copies + 1 })}
|
||||
onDecrement={() => props.onUpdate?.(file.id, { copies: Math.max(1, file.copies - 1) })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormatPicker
|
||||
bind:open={formatOpen}
|
||||
options={FORMAT_OPTIONS}
|
||||
selected={file.format ?? 'Авто'}
|
||||
onselect={(v) => props.onUpdate?.(file.id, { format: v })}
|
||||
/>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
@ -138,6 +186,9 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.duo-group + .slider-options { margin-top: -1px; }
|
||||
.duo-row { display: flex; gap: 5px; width: 100%; }
|
||||
.duo-group { display: flex; flex-direction: column; gap: 6px; width: 100%; }
|
||||
.action-btn { width: 100%; padding: 16px 24px; border-radius: 16px; font-size: 16px; font-weight: 700; cursor: pointer; border: none; color: #fff; transition: opacity 0.2s; }
|
||||
.action-btn:disabled { opacity: 0.6; cursor: wait; }
|
||||
.action-btn.primary { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); box-shadow: 0 8px 24px -6px rgba(99,102,241,0.5); }
|
||||
@ -156,22 +207,16 @@
|
||||
.toggle-group { display: flex; gap: 10px; }
|
||||
.toggle-btn { flex: 1; padding: 12px 8px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.08); background: rgba(255,255,255,0.05); color: #f5f7fa; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.18s ease; }
|
||||
.toggle-btn.active { background: rgba(99,102,241,0.25); border-color: #6366f1; color: #fff; }
|
||||
.quality-sub { gap: 8px; }
|
||||
.quality-slider { -webkit-appearance: none; width: 100%; height: 6px; border-radius: 4px; background: rgba(255,255,255,0.12); outline: none; }
|
||||
.quality-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #6366f1; cursor: pointer; border: 2px solid #fff; }
|
||||
.quality-marks-inline { display: flex; justify-content: space-between; font-size: 11px; color: #8b93a1; margin-top: 4px; }
|
||||
.slider-options { display: flex; flex-direction: column; gap: 7px; width: 100%; }
|
||||
.cycle-row { display: flex; gap: 10px; width: 100%; }
|
||||
.copies-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.06); }
|
||||
.file-card { border-radius: 16px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); overflow: hidden; padding-bottom: 6px; }
|
||||
.file-row { width: 100%; box-sizing: border-box; display: flex; align-items: center; gap: 12px; padding: 14px; background: none; border: none; color: #f5f7fa; cursor: pointer; user-select: none; text-align: left; }
|
||||
.file-row:hover { background: rgba(255,255,255,0.03); }
|
||||
.chevron { color: #6366f1; font-size: 12px; transition: transform 0.15s; }
|
||||
.file-name { flex: 1; font-size: 15px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.file-row-actions { display: flex; gap: 6px; }
|
||||
.file-options { padding: 8px 14px 14px; display: flex; flex-direction: column; gap: 18px; }
|
||||
.print-params-row { flex-direction: row; align-items: center; justify-content: space-between; gap: 12px; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.06); margin-bottom: 4px; }
|
||||
.pages-control { display: flex; align-items: center; flex: 1; min-width: 0; }
|
||||
.pages-btn { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.1); color: #fff; border-radius: 8px; padding: 6px 16px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.15s ease; }
|
||||
.pages-btn:hover { background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.2); }
|
||||
.copies-control-inline { display: flex; align-items: center; flex-shrink: 0; }
|
||||
.file-options { padding: 8px 14px 14px; display: flex; flex-direction: column; gap: 12px; }
|
||||
.empty-upload { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; text-align: center; }
|
||||
.empty-hint { color: #8b93a1; font-size: 15px; margin: 0; }
|
||||
.actions { padding: 16px 20px 32px; display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
Reference in New Issue
Block a user