@substrate-system/image-editor
v0.0.5
Published
[](https://github.com/substrate-system/package/actions/workflows/nodejs.yml) [This extends web-component,
so it has ImageEditor.TAG, ImageEditor.event('edit'),
and on() and off() methods.
import { ImageEditor } from '@substrate-system/image-editor'
const editor = document.querySelector(ImageEditor.TAG)
// event type 'image-editor:edit'
editor.on('edit', event => {
console.log(event.detail.img)
})You can use addEventListener() with the full namespaced event names shown in
the Events section.
Attributes
All attributes are reflected on the element. Kebab-case numeric attributes also have typed camel-case properties.
free-form
Boolean attribute. It is absent by default. When present, each resize axis is constrained independently instead of preserving the image aspect ratio.
<image-editor free-form>
<img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>The equivalent property is editor.freeForm:
editor.freeForm = true
editor.freeForm = falseThe resize mode is captured when a pointer or keyboard resize starts. A mode change therefore applies to the next interaction.
min-width and min-height
Positive minimum dimensions in CSS pixels. Both default to 50.
<image-editor min-width="120" min-height="90">
<img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>The typed properties are editor.minWidth and editor.minHeight:
editor.minWidth = 120
editor.minHeight = 90Missing, non-positive, non-finite, or otherwise invalid values resolve to the
50-pixel default when read. In aspect-ratio mode, both minimums are honored
without distorting the image. In free-form mode, each axis is clamped
independently.
visible
Controls when the resize outline and handles are disclosed. Accepted values are:
alwayskeeps the outline and handles visible.hoverhides the handles until the editor is hovered or focused.touchkeeps them visible on touch devices and uses hover disclosure on other devices.
The default is touch. Missing or unsupported values also resolve to touch.
<image-editor visible="hover">
<img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>The typed property is editor.visible:
editor.visible = 'always'Changing the attribute or property updates an already-rendered editor.
Events
Events bubble from the element, are cancelable, and use the
image-editor:<name> naming convention. The examples below use the native
event API.
image-editor:resize-start
Emitted when a pointer resize begins or when the first arrow key starts a keyboard resize sequence. The event has no detail payload.
editor.addEventListener('image-editor:resize-start', () => {
console.log('resize started')
})image-editor:resize
Emitted with the current dimensions. Keyboard resizing emits this event for each arrow-key change. Pointer resizing emits it once after a dragged pointer is released.
The detail contains rounded CSS-pixel dimensions:
editor.addEventListener('image-editor:resize', event => {
const { width, height, blob } = event.detail
console.log(width, height)
if (blob) {
// A canvas-generated Blob is available when canvas conversion worked.
upload(blob)
}
})For pointer resizing, blob may be included in the detail after the canvas
conversion completes. If canvas conversion cannot produce a blob, the detail
still contains width and height.
image-editor:resize-end
Emitted when a keyboard resize sequence is committed on keyup and canvas conversion returns a blob. Its detail is always:
{
blob: Blob,
width: number,
height: number
}The blob is drawn at the same pixel dimensions reported in the detail. For
images loaded from another origin, configure CORS on the image response and
use an appropriate crossorigin value before loading the image if your
application needs blob output.
image-editor:edit
Emitted when the pencil button is clicked. The event is cancelable and its detail contains the captured image element:
editor.addEventListener('image-editor:edit', event => {
const image = event.detail.img
openImageEditor(image)
})The component does not open a dialog or modify the image for you.
image-editor:alt
Emitted when the ALT badge is clicked. The event is cancelable and its detail
contains the current alt value and the captured image:
editor.addEventListener('image-editor:alt', event => {
const { alt, img } = event.detail
openAltTextEditor({ alt, img })
})An absent alt attribute is reported as an empty string. The badge displays
+ALT for an absent or empty value and ALT for a non-empty value. It updates
when the image's alt attribute changes.
Keyboard resizing
After a handle receives focus:
- Arrow keys change the size by 10 pixels.
- Shift plus an arrow key changes the size by 50 pixels.
Escaperestores the inline width and height from before the sequence.- Releasing an arrow key commits the current keyboard resize sequence.
Keyboard resizing uses the same aspect-ratio and minimum-size rules as pointer
resizing. Escape cancels the sequence and does not produce a resize-end
event.
Styling
The component renders in the light DOM. Import the package stylesheet to get the outline, controls, handles, and undefined-element guard:
import '@substrate-system/image-editor/css'The minified stylesheet is available at:
import '@substrate-system/image-editor/min/css'CSS custom properties
Defaults are defined globally on :root. Override them after importing the
package stylesheet:
:root {
--image-editor-outline-color: rebeccapurple;
--image-editor-handle-bg: white;
--image-editor-button-bg: rgb(0 0 0 / 65%);
}Available properties are:
--image-editor-outline-color: visible outline color; defaultblack.--image-editor-outline-hidden-color: hidden outline color; defaulttransparent.--image-editor-outline-width: outline width; default2px.--image-editor-outline-style: outline style; defaultdashed.--image-editor-handle-size: square handle size; default10px.--image-editor-handle-bg: handle fill color; defaultwhite.--image-editor-handle-border: handle border color; defaultblack.--image-editor-handle-border-width: handle border width; default1px.--image-editor-button-size: button height and icon-button width; default2rem.--image-editor-button-icon-size: pencil icon size; default1.125rem.--image-editor-button-bg: button background; default 65% black.--image-editor-button-bg-hover: hovered button background; default 80% black.--image-editor-button-focus-color: focus-ring color; default#1d9bf0.--image-editor-button-padding: edit-button padding; default0.--image-editor-button-icon-stroke-width: pencil stroke width; default2px.--image-editor-overlay-padding: overlay inset; default8px.
Avoiding undefined-content flash
The stylesheet hides image-editor until its custom element definition is
available. If your page has several custom elements, you can also hide the
whole page until they are defined:
<html class="reduce-fouce">
<head>
<style>
html.reduce-fouce { opacity: 0; }
</style>
<noscript>
<style>
html.reduce-fouce { opacity: 1 !important; }
</style>
</noscript>
</head>
</html>Reveal the page after the definition is ready, with a timeout so a failed definition cannot leave the page hidden forever:
await Promise.race([
customElements.whenDefined('image-editor'),
new Promise(resolve => setTimeout(resolve, 2000))
])
document.documentElement.classList.remove('reduce-fouce')Pre-built files
The package publishes JavaScript and CSS files in dist/:
index.jsandindex.min.jsare ESM.index.cjsandindex.min.cjsare CommonJS.index.cssandindex.min.cssare the stylesheets.
If your application does not bundle npm packages, copy the minified files to a directory served by your web server:
cp node_modules/@substrate-system/image-editor/dist/index.min.js public/
cp node_modules/@substrate-system/image-editor/dist/index.min.css public/Then load the stylesheet and module in HTML:
<link rel="stylesheet" href="/index.min.css">
<script type="module" src="/index.min.js"></script>Development
Install dependencies and start the Vite example app:
npm install
npm startThe example app runs on port 2222 by default. Useful project commands are:
npm test # Run browser tests
npm run lint # Run ESLint
npm run build # Build JavaScript and CSS artifactsThe package build writes publishable artifacts to dist/. The example build
uses npm run build-example and writes its output to public/.
