|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 4 months ago | |
| image-picker.css | 4 months ago | |
| image-picker.svelte | 4 months ago | |
| index.ts | 4 months ago | |
| types.ts | 4 months ago | |
README.md
ImagePicker (eidos)
The styled, configured image picker — a pure composition of three already-built
framework components: FileUpload (drop + click) → Image (preview, fit modes) +
a rotate/remove toolbar + ImageAdjustments (filters). The consumer uses the
configured root; the empty/ready states + the toolbar are rendered internally.
<script>
import ImagePicker from '$uix/eidos/components/image-picker';
let file = $state(null);
</script>
<ImagePicker bind:file onChange={(v) => save(v)} />
Baseline
No Air baseline. ImagePicker is a new component (2026-06-10); there is no
air/ predecessor to recover. It was designed fresh against the references below,
not ported.
Comparativa
Audited against the visual reference (Untitled UI) + the headless / file references:
| Capability | Untitled UI | Chakra | Ark UI | Radix | MUI | → UIX |
|---|---|---|---|---|---|---|
| Image picker primitive | ✅ image-pickers | ❌ | ❌ | ❌ | ❌ | ✅ |
| File-upload base | ✅ | ✅ FileUpload | ✅ FileUpload | ❌ | ❌ (base-ui open issue) | ✅ reuse file-upload |
| Preview + fill modes | ✅ fill/fit/crop/tile | ❌ | ❌ | ❌ | ❌ | ✅ reuse image (cover/contain/fill) |
| 90° rotation | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Filter / effect sliders | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ reuse ImageAdjustments |
| Crop | ❌ (separate) | ❌ | ✅ image-cropper |
❌ | ❌ | hand-off (next component) |
| Sound / haptic on actions | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ sema |
No mainstream component library ships an image picker as a primitive — it's app code over a file input. UIX composes three resolved components and emits a cropper-ready value.
What it composes
| State | Renders |
|---|---|
| empty | <FileUpload> (drop + click), accept="image/*", single file |
| ready | <Image> preview + a floating rotate/remove toolbar + <ImageAdjustments> |
Props
ProviderProps (soma) + composition knobs:
| Prop | Type | Default | Notes |
|---|---|---|---|
file / fit / rotation / adjustments |
— | — | bindable — see soma README |
onChange |
(v: ImagePickerValue) => void |
— | cropper-ready value |
disabled |
boolean |
false |
→ both FileUpload + controls |
accept |
string |
'image/*' |
→ FileUpload |
maxSize |
number |
— | → FileUpload |
size |
'sm' | 'md' | 'lg' |
'sm' |
embedded ImageAdjustments scale |
showAdjustments |
boolean |
true |
render the filters panel |
Decisiones
- Composition over reimplementation. ImagePicker renders
<FileUpload>/<Image>/<ImageAdjustments>— it never re-declares a dropzone, an image surface or a slider. The morfo declares only the picker's OWN orchestration. rotateis ahandleverb. Spatial manipulation (with drag / resize / scroll), even though a discrete button triggers it — that's its canonical family. select / remove arecommit.- Object-URL lives in soma. Created on
filechange and revoked via the effect cleanup (previous URL on replace, last on unmount).createObjectURLis used directly, matching thefile-upload/wordsprecedent (it is not an ActiveDom concern). data-fitis reflected on the picker even though<Image>owns the visualobject-fit. It is a picker-level state reflection (parallel todata-rotation/data-state) so tooling and consumer CSS can read the picker's fit from its own DOM. Chosen over removing it (which would hide the state) and over a contrived CSS rule.- Rotation rotates a
.canvaswrapper, not the toolbar. The toolbar is a sibling of the canvas inside the Preview, so it stays upright while the image rotates.
Recipe tokens
Public, themeable — --image-picker-* (the composed components bring their own):
| Token | Default |
|---|---|
gap |
space-3 (preview → adjustments) |
preview-radius / preview-bg / preview-border / preview-aspect |
rounded box, 4/3 |
toolbar-gap / toolbar-inset |
floating top-right |
button-* |
icon-button chrome (rotate / remove) |
eidos-lint image-picker → invalid 0 (15 morfo-backed selectors). CSS is imported
from the wrapper (code-split chunk) — intentionally NOT in eidos/index.css.
Gaps
| Gap | Disposition | Notes |
|---|---|---|
| Crop (region selection) | descartar here | Belongs to the separate Ark-style image-cropper; ImagePicker hands it url/file. |
| Rotation letterbox at 90°/270° | diferir | The preview box doesn't swap dimensions, so a non-square image letterboxes. Revisit when the cropper lands. |
highlights / shadows tone curves |
diferir | Need canvas / SVG feComponentTransfer; tracked in ImageAdjustments. |
| Multiple images / gallery | descartar | Single image by design (multiple={false}); use file-upload directly for multi. |
| WAI-ARIA APG pattern | diferir | No single APG pattern covers a file-picker composite; the dropzone is a button, the root a labelled group. |