Security News
Research
Supply Chain Attack on Rspack npm Packages Injects Cryptojacking Malware
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
@annotation-studio/components
Advanced tools
These are React components that are composed of other react components. They are still mostly pure components, handing off their state management to Redux. Some offer APIs for choosing between different components (like selectors) with the goal in the future to be smart and delegate based on different contexts to choose the best component for the job. (Think A/V IIIF content)
This is the abstract concept of space occupied so far by Selectors, Viewers and drawn Regions or annotations.
propType = {
onUpdateSelector: PropTypes.func,
onSaveSelector: PropTypes.func,
selector: PropTypes.shape({
type: PropTypes.string.isRequired,
left: PropTypes.number,
top: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
}),
image: PropTypes.shape({
src: PropTypes.string.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
}).isRequired,
displayWidth: PropTypes.number.isRequired,
}
<Canvas image={{ src: '...', height: 100, width: 100 }} displayWidth={50}/>
The confirmation form is a composite of form elements and display values. It adds a "preview" to forms to allow a better and customisable representation of the values.
propTypes = {
draft: PropTypes.shape({
id: PropTypes.string.isRequired,
isPreviewing: PropTypes.bool.isRequired,
input: PropTypes.object.isRequired,
}),
bem: PropTypes.object,
nextLabel: PropTypes.string,
editLabel: PropTypes.string,
saveLabel: PropTypes.string,
updateField: PropTypes.func.isRequired,
onEdit: PropTypes.func,
onSave: PropTypes.func,
previewDraft: PropTypes.func.isRequired,
};
<ConfirmationForm
draft={{ id: 1, isPreviewing: this.state.previewing, input: { title: 'testing' } }}
updateField={e => console.log(e)}
previewDraft={e => this.setState({ previewing: true })}
onEdit={e => this.setState({ previewing: false })}
/>
This is the composite selector that will be most used in apps to get any selector that you require without duplicating code elsewhere.
propTypes = {
type: PropTypes.string.isRequired,
onSave: PropTypes.func.isRequired,
onCancel: PropTypes.func,
initialPosition: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
}),
}
<Selector type="WholeCanvasSelector"/>
The router is a very basic routing component that will route in a similar way to a switch/case or if/elseif. You can define boolean logic to create complex flows through components, while remaining pure and quick.
propTypes = {
routes: PropTypes.arrayOf(
PropTypes.shape({
matches: PropTypes.func.isRequired,
render: PropTypes.func.isRequired,
}).isRequired
),
state: PropTypes.any,
renderNotFound: PropTypes.func,
};
<Router
routes={[
{
matches: (state) => state.isFirst,
render: () => <StepOne />
},
{
matches: (state) => !state.isFinished && !state.isFirst,
render: () => <StepTwo />
},
{
matches: (state) => state.isFinished,
render: () => <LastStep />
}
]}
state={{ isFirst: true }}
/>
Note: this is likely to be removed from this part of the library as it is not strictly a component.
This is a library that will try and validate a variety of JSON-LD fields and contexts.
import validation from '@annotation-studio/components/es/domain/validation';
const fields = [
{context: 'https://schema.org/jobTitle', value: 'Some job title'},
{context: 'https://schema.org/birthDate', value: '31st Feb 1990'},
];
function validate(field) {
if (validation[field.context]) {
return validation[field.context](field.value);
}
return true;
}
function validateAll(fields) {
return fields.reduce((acc, cur) => acc && validate(field))
}
const isValid = validateAll(fields); // False since 31st Feb is invalid.
Inputs are combined by a unified input field, offering an easy to compose interface to render a variety of fields. In the future this will be expanded to be more smart when choosing which field to render. Currently we have 3 example fields.
The date picker is a wrapper around a 3rd-party React component that really was meant to show off how these interfaces can be driven and expanded without re-inventing various wheels.
propTypes = {
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
update: PropTypes.func.isRequired,
metaData: PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string
}).isRequired,
preview: PropTypes.bool,
};
<DatePicker
id={123}
value={this.state.value}
update={(value) => this.setState({ value })}
metaData={{ title: 'Pick a date' }}
/>
List of classes:
.input-text-box
.input-text-box__label
.input-text-box__description
.input-text-box__preview
.input-text-box__input
.input-text-box__input--datepicker
The form is a composition of form elements, with associated meta data to drive both styling and display of elements. This will also be expanded to be more introspective of fields and perform "best guess" when choosing fields. Currently its all driven by type.
propTypes = {
draft: PropTypes.shape({
id: PropTypes.string.isRequired,
input: PropTypes.objectOf(PropTypes.string).isRequired,
isPreviewing: PropTypes.bool
}).isRequired,
fields: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
inputType: PropTypes.oneOf(['madoc:textarea', 'madoc:datepicker', 'madoc:textbox']),
metaData: PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string,
conformsTo: PropTypes.shape({
id: PropTypes.oneOf([
'http://xmlns.com/foaf/name',
'https://schema.org/birthDate',
'https://schema.org/jobTitle',
'http://dublincore.org/documents/dcmi-terms/#terms-description'
]),
label: PropTypes.string.isRequired,
}),
}).isRequired,
})).isRequired,
updateField: PropTypes.func.isRequired,
};
<Form
draft={{ id: 1, input: this.state.values || {name: '', familyName: ''} }}
fields={[
{ id: 'name', inputType: 'madoc:textbox', metaData: {
title: 'First name'
}},
{ id: 'familyName', inputType: 'madoc:textbox', metaData: {
title: 'Family name'
}},
]}
updateField={(field, value) => this.setState(s => s.values[field.id] = value)}
/>
List of classes:
.input-form
.input-form__error
.input-form__error--hidden
The text area is one of the most basic components binding to the DOM.
propTypes = {
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
update: PropTypes.func.isRequired,
metaData: PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string
}).isRequired,
preview: PropTypes.bool,
};
<TextArea
id={123}
value={this.state.value}
update={(value) => this.setState({ value })}
metaData={{ title: 'Tell a story' }}
/>
List of classes:
.input-text-box
.input-text-box__label
.input-text-box__description
.input-text-box__preview
.input-text-box__input
.input-text-box__input--textarea
The text area is one of the most basic components binding to the DOM.
propTypes = {
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
update: PropTypes.func.isRequired,
metaData: PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string
}).isRequired,
preview: PropTypes.bool,
};
<TextArea
id={123}
value={this.state.value}
update={(value) => this.setState({ value })}
metaData={{ title: 'First name' }}
/>
List of classes:
.input-text-box
.input-text-box__label
.input-text-box__description
.input-text-box__preview
.input-text-box__input
The navigation section is one of the latest additions.
The resource navigator focuses on building a UI around navigating through state by providing a path similar to path1.path2.path3
in Object notation. It binds to a UI and logic, allowing for state to be changed and the user to navigate through and render various things based on the current "location" in the state.
propTypes = {
selections: PropTypes.arrayOf(PropTypes.any).isRequired,
bem: PropTypes.object,
renderSelection: PropTypes.func,
onSelect: PropTypes.func.isRequired,
};
<ResourceNavigator
selections={['apples', 'oranges']}
onSelect={selection => console.log(selection)}>
Choose a fruit
</ResourceNavigator>
List of classes:
.resource-navigator
.resource-navigator__selection
Selectors are built around highlighting a section or region of an image using a variety of tools, and being able to generate W3C valid selectors. This includes the media frags selector and also the SVG selector specifications.
The box selector is a draggable box that is initially placed on a canvas. It is dragged around and is responsible for relaying back the co-ordinates that it currently exists at on the canvas (relative to the view port, for OSD)
propTypes = {
onSave: PropTypes.func.isRequired,
onUpdate: PropTypes.func.isRequired,
onCancel: PropTypes.func,
initialPosition: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
}),
};
<BoxSelector
onSave={position => console.log(position)}
onUpdate={position => console.log(position)}
/>
Note: the box selector can be dragged around its parents bounds. Make sure you have an appropriate container for dragging the box around and that it matches the height and width of your content.
List of classes:
.box-selector
.box-selector__button
The whole canvas selector is a unique component, as it doesn't require a UI to work. Its job is simply to conform to the interface of other selectors and provide a way for the onSave method to be called to let the application know that it needs to target the whole application.
There is support for making the user confirm their selection before hand. You could envision cancel buttons being added at an application level too, if that fits with the user experience.
propTypes = {
skipConfirm: PropTypes.bool,
onSave: PropTypes.func.isRequired,
onCancel: PropTypes.func, // unused
};
<WholeCanvasSelector onSave={() => console.log('called on render')} />
Viewers are components that display various kinds of content that you might want to annotate or surface annotations on.
This is the most basic viewer, it takes an image, and displays it full size.
propTypes = {
resource: PropTypes.string.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
regions: PropTypes.arrayOf(
PropTypes.shape({
onClick: PropTypes.func,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
selected: PropTypes.bool.isRequired,
})
)
}
<StaticImageViewer
resource="http://.../portrait.jpg"
height={200}
width={100}
/>
This was the next iteration from the static image viewer, this component internally uses the static image viewer, but simply scales it down. You must specify a scale for this one to work. Note: because of the way react works, you can change the height, width and scale interactively and still achieve accurate positions of regions, this aids with responsive design
propTypes = {
resource: PropTypes.string.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
regions: PropTypes.arrayOf(
PropTypes.shape({
onClick: PropTypes.func,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
selected: PropTypes.bool.isRequired,
})
),
scale: PropTypes.number.isRequired // this is unique to this component.
}
<ScaledImageViewer
resource="http://.../portrait.jpg"
height={400}
width={200}
scale={0.5}
/>
This will display the image at half the size (i.e. 100x200). You can also scale up, if required.
This is the most common choice for viewer, it consumes IIIF resources and enables deep zoom functionality. Unfortunately, not all content is the same, so this viewer will fallback as often as it can if it cannot read the resource provided.
propTypes = {
resource: PropTypes.string.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
regions: PropTypes.arrayOf(
PropTypes.shape({
onClick: PropTypes.func,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
selected: PropTypes.bool.isRequired,
})
),
scale: PropTypes.number.isRequired,
canvas: PropTypes.object.isRequired, // this is the IIIF Canvas, fully loaded, not an ID.
maxHeight: PropTypes.number // Defaults to height*scale
}
<OpenSeadragonViewer
resource="http://.../portrait.jpg" // this is required in case a fallback is required
height={400}
width={200}
scale={0.5}
canvas={{ '@id': '...', '@type': 'sc:Canvas', /*...*/ }}
/>
FAQs
annotation-components React component
The npm package @annotation-studio/components receives a total of 33 weekly downloads. As such, @annotation-studio/components popularity was classified as not popular.
We found that @annotation-studio/components demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.
Security News
Sonar’s acquisition of Tidelift highlights a growing industry shift toward sustainable open source funding, addressing maintainer burnout and critical software dependencies.