feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
/**
|
|
|
|
|
|
* Cropper recipe — the crop UI chrome: framed viewport, darken-outside mask,
|
|
|
|
|
|
* rule-of-thirds grid, and corner handles. The crop geometry is inline style on
|
|
|
|
|
|
* the Selection (set by soma); the image is the composed `<Image>`.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Tokens: `--cropper-*` (public, themeable) from `recipes/base.ts`.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper] {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper][data-disabled] {
|
feat(eidos): canonize theming scales + tokenize magic numbers
Audit-driven canonization of the eidos theming layer: a canonical scale that
components bypass with literals drifts into N variants. Every axis is now
retunable-by-theme AND consumed via token, never a bare literal.
Bloque A/B (scales): --blur-* (Tailwind-aligned, depth planes consume it);
inner-shadow --shadow-inset-* (mode-aware) for the recessed plane; gradient
angle/named tokens; --breakpoint-* sourced from ActiveDom (runtime, dev-settable)
+ container queries; opacity dual numeric+semantic scale; border-width
none/thin/medium/thick/heavy (adds real 3px); tracking caps/widest. Fase 7:
3 size archetypes documented + coherence guard (no px/rem in recipe
font-size/icon-size).
Bloque C (magic numbers): z-index 80/50/99 (combobox/nav-menu/drag-drop) ->
recipe content-z/preview-z tokens (overlay micro-band soma mirrors); on-scale
durations -> var(--duration-*) (dialog fast/slow, card slow); 12 single
border/ring widths -> var(--border-width-*), value-preserving. Off-scale kept
only as justified recipe tokens (continuous spinner/loading periods, sub-fast
press). Self-contained dimensional scales (avatar ring, ring-thickness) left whole.
Docs: THEMING.md SS35 + SS6 + SS29, THEMING_NOTES, README, TSC. Excludes
words/palabras/chronos (active dev tracks).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
opacity: var(--opacity-disabled);
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper-viewport] {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
/* aspect-ratio is set inline from the image's natural size. Fallback 1:1. */
|
|
|
|
|
|
aspect-ratio: 1;
|
|
|
|
|
|
border-radius: var(--cropper-viewport-radius);
|
|
|
|
|
|
background: var(--cropper-viewport-bg);
|
|
|
|
|
|
/* The image fills the viewport; no scrolling while dragging the selection. */
|
|
|
|
|
|
touch-action: none;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.eidos-cropper-image {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
block-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Stop the browser's native image drag-and-drop, which would cancel the pan. */
|
|
|
|
|
|
[data-cropper-viewport] img {
|
|
|
|
|
|
-webkit-user-drag: none;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The crop rectangle. The huge box-shadow darkens everything outside it
|
|
|
|
|
|
(clipped by the viewport's overflow:hidden) — it follows border-radius, so a
|
|
|
|
|
|
round selection cuts a circular hole. */
|
|
|
|
|
|
[data-cropper-selection] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
border: var(--cropper-selection-border-width) solid var(--cropper-selection-border);
|
|
|
|
|
|
box-shadow: 0 0 0 9999px var(--cropper-mask-bg);
|
|
|
|
|
|
cursor: move;
|
|
|
|
|
|
touch-action: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper-selection][data-shape='round'] {
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper-selection][data-dragging] {
|
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* When zoomed, the background image shows a `grab` cursor (drag it to pan / set
|
|
|
|
|
|
the focus). The selection keeps `move` — it repositions the crop area. */
|
|
|
|
|
|
.eidos-cropper-viewport.is-zoomed .eidos-cropper-image {
|
|
|
|
|
|
cursor: grab;
|
|
|
|
|
|
}
|
|
|
|
|
|
.eidos-cropper-viewport.is-zoomed .eidos-cropper-image:active {
|
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Rule-of-thirds — four thin lines at 1/3 and 2/3 on each axis. */
|
|
|
|
|
|
[data-cropper-grid] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
background:
|
|
|
|
|
|
linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 33.33% 0 / 1px 100% no-repeat,
|
|
|
|
|
|
linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 66.66% 0 / 1px 100% no-repeat,
|
|
|
|
|
|
linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 0 33.33% / 100% 1px no-repeat,
|
|
|
|
|
|
linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 0 66.66% / 100% 1px no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The thirds grid reads as noise inside a circular crop — hide it there. */
|
|
|
|
|
|
[data-cropper-selection][data-shape='round'] [data-cropper-grid] {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper-handle] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
inline-size: var(--cropper-handle-size);
|
|
|
|
|
|
block-size: var(--cropper-handle-size);
|
|
|
|
|
|
border: var(--cropper-handle-border-width) solid var(--cropper-handle-border);
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--cropper-handle-bg);
|
|
|
|
|
|
touch-action: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-cropper-handle][data-corner='nw'] {
|
|
|
|
|
|
inset-block-start: 0;
|
|
|
|
|
|
inset-inline-start: 0;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
cursor: nwse-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='ne'] {
|
|
|
|
|
|
inset-block-start: 0;
|
|
|
|
|
|
inset-inline-end: 0;
|
|
|
|
|
|
transform: translate(50%, -50%);
|
|
|
|
|
|
cursor: nesw-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='se'] {
|
|
|
|
|
|
inset-block-end: 0;
|
|
|
|
|
|
inset-inline-end: 0;
|
|
|
|
|
|
transform: translate(50%, 50%);
|
|
|
|
|
|
cursor: nwse-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='sw'] {
|
|
|
|
|
|
inset-block-end: 0;
|
|
|
|
|
|
inset-inline-start: 0;
|
|
|
|
|
|
transform: translate(-50%, 50%);
|
|
|
|
|
|
cursor: nesw-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Edge handles — centered on each side. */
|
|
|
|
|
|
[data-cropper-handle][data-corner='n'] {
|
|
|
|
|
|
inset-block-start: 0;
|
|
|
|
|
|
inset-inline-start: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
cursor: ns-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='s'] {
|
|
|
|
|
|
inset-block-end: 0;
|
|
|
|
|
|
inset-inline-start: 50%;
|
|
|
|
|
|
transform: translate(-50%, 50%);
|
|
|
|
|
|
cursor: ns-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='e'] {
|
|
|
|
|
|
inset-block-start: 50%;
|
|
|
|
|
|
inset-inline-end: 0;
|
|
|
|
|
|
transform: translate(50%, -50%);
|
|
|
|
|
|
cursor: ew-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-cropper-handle][data-corner='w'] {
|
|
|
|
|
|
inset-block-start: 50%;
|
|
|
|
|
|
inset-inline-start: 0;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
cursor: ew-resize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Zoom controls (− slider + reset) ───────────────────────────────── */
|
|
|
|
|
|
.eidos-cropper-zoom {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--cropper-zoom-gap);
|
|
|
|
|
|
margin-block-start: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.eidos-cropper-zoom [data-slider] {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.eidos-cropper-zoom-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
inline-size: var(--cropper-zoom-btn-size);
|
|
|
|
|
|
block-size: var(--cropper-zoom-btn-size);
|
feat(eidos): canonize theming scales + tokenize magic numbers
Audit-driven canonization of the eidos theming layer: a canonical scale that
components bypass with literals drifts into N variants. Every axis is now
retunable-by-theme AND consumed via token, never a bare literal.
Bloque A/B (scales): --blur-* (Tailwind-aligned, depth planes consume it);
inner-shadow --shadow-inset-* (mode-aware) for the recessed plane; gradient
angle/named tokens; --breakpoint-* sourced from ActiveDom (runtime, dev-settable)
+ container queries; opacity dual numeric+semantic scale; border-width
none/thin/medium/thick/heavy (adds real 3px); tracking caps/widest. Fase 7:
3 size archetypes documented + coherence guard (no px/rem in recipe
font-size/icon-size).
Bloque C (magic numbers): z-index 80/50/99 (combobox/nav-menu/drag-drop) ->
recipe content-z/preview-z tokens (overlay micro-band soma mirrors); on-scale
durations -> var(--duration-*) (dialog fast/slow, card slow); 12 single
border/ring widths -> var(--border-width-*), value-preserving. Off-scale kept
only as justified recipe tokens (continuous spinner/loading periods, sub-fast
press). Self-contained dimensional scales (avatar ring, ring-thickness) left whole.
Docs: THEMING.md SS35 + SS6 + SS29, THEMING_NOTES, README, TSC. Excludes
words/palabras/chronos (active dev tracks).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
border: var(--border-width) solid var(--cropper-zoom-btn-border);
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
|
background: var(--cropper-zoom-btn-bg);
|
|
|
|
|
|
color: var(--cropper-zoom-btn-fg);
|
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.eidos-cropper-zoom-btn:hover {
|
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.eidos-cropper-zoom-btn:focus-visible {
|
|
|
|
|
|
outline: var(--focus-ring-width) solid var(--focus-ring-color);
|
|
|
|
|
|
outline-offset: 0;
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
}
|