npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@myrmidon/cadmus-img-gallery

v6.0.0

Published

Cadmus - minimalist image gallery.

Downloads

23

Readme

Cadmus Image Gallery

  • 🔑 @myrmidon/cadmus-img-gallery

This library was generated with Angular CLI version 17.3.0.

Overview

The image gallery is a minimalist set of components wrapping Annotorious functionalities and used to:

  • show a gallery of images from some online source.
  • let users pick an image and annotate it.

The idea is having a Cadmus part to create annotations on images. Once you create these annotations, you will be able to use other parts to link them to any type of specialized data via their ID.

These components are independent from any specific servicing technology; images could be served by IIIF, cloud stores, etc.; you are free to provide your own service (implementing GalleryService) to get a page of images, and a single image, with the desired size. Even the filters used to browse the list of images are totally open, so that you can define as many filters as you want, according to your own data.

Annotation IDs

As usual in Cadmus, annotation IDs are opaque (and so they are supposed to be), just like items or parts IDs. Yet, at least in the context of the editor, more user-friendly IDs are welcome.

As explained here, in other Cadmus parts, user friendly IDs are got via EID's (entity IDs); these are:

  • aliases of entities which already have their own global ID (a GUID), like items;
  • identifiers assigned to entities inside a part.

In both cases, they can be referenced via a lookup UI, where you just type some characters belonging to the ID to get a list of the first matching IDs. To apply the same approach in the context of image annotations, we define a convention by which you can assign an EID to any annotation via a tag beginning with eid_. For instance, if you draw a rectangle around some region of an image, and assign it the tag eid_sample, by convention this means that the annotation linked to that region will get an EID equal to sample.

Usage

Typically you create your own gallery component to orchestrate children components for the gallery list and its annotator. Thus, this is essentially a wrapper for lower level components.

To explain the usage of these components I will refer to the example page in this app, describing it from top to bottom.

Components Layout

App Configuration

(1) ensure to configure INDEX_LOOKUP_DEFINITIONS if you are going to use UI lookup components targeting annotations IDs, e.g.:

// in appConfig:

// for lookup in asserted IDs - note that this would require a backend
const INDEX_LOOKUP_DEFINITIONS: IndexLookupDefinitions = {
  item_eid: {
    typeId: 'it.vedph.metadata',
    name: 'eid',
  }
};

(2) ensure to configure default options for Material dialogs, as annotations metadata are edited inside a popup, e.g.:

// in appConfig providers array:
{
  provide: MAT_DIALOG_DEFAULT_OPTIONS,
  useValue: {
    hasBackdrop: true,
    maxHeight: '800px',
  },
},

(3) configure the gallery providers, e.g.:

// in appConfig providers array:

// if you want to use the mock gallery, uncomment these two providers
// and comment the IIIF ones below.
// mock image gallery
// {
//   provide: IMAGE_GALLERY_SERVICE_KEY,
//   useClass: MockGalleryService,
// },
// {
//   provide: IMAGE_GALLERY_OPTIONS_KEY,
//   useValue: {
//     baseUri: '',
//     count: 50,
//     width: 300,
//     height: 400,
//   },
// },
// IIIF image gallery
{
  provide: IMAGE_GALLERY_SERVICE_KEY,
  useClass: SimpleIiifGalleryService,
},
{
  provide: IMAGE_GALLERY_OPTIONS_KEY,
  useValue: {
    baseUri: '',
    manifestUri:
      'https://dms-data.stanford.edu/data/manifests/Parker/xj710dc7305/manifest.json',
    arrayPath: 'sequences[0]/canvases',
    resourcePath: 'images[0]/resource',
    labelPath: 'label',
    width: 300,
    height: 400,
    targetWidth: 800,
    targetHeight: -1,
    pageSize: 6,
    // skip: 6
  } as SimpleIiifGalleryOptions,
},

Top Level Page

In our demo, the page component wrapping a fully functional gallery image annotator is at the top level. Its role is just providing a page wrapping the functionality to be shown in the demo app.

This page component gets injected:

  • a gallery service (implementing interface GalleryService). This is injected via a token (IMAGE_GALLERY_SERVICE_KEY).
  • the service for managing the gallery options. (GalleryOptionsService). The default options are provided by the IMAGE_GALLERY_OPTIONS_KEY injection token. You can then update them, get them, or subscribe to them.

The component has these properties:

  • image (GalleryImage): the current image being annotated, if any.
  • annotations (GalleryImageAnnotation[]): the annotations on the current image.
  • actions (BarCustomAction[]): the custom actions related to annotations. In this component custom actions are just mock; when invoked, the component will just log the corresponding action object. In a real world application you will take proper action according to it.

The component handles these events emitted by its descendants:

  • image pick: when an image is picked from the gallery, the gallery service is used to retrieve it into image.
  • annotations change: the annotations list is updated.
  • action request: just dump the requested action.

So this is just a wrapper to provide a page binding app-wide services to the gallery. Its template contains a child component representing the gallery image annotator (app-my-gallery-image-annotator).

Custom Gallery Image Annotator

This is the annotator wrapped by the top level page component in the demo. It contains a gallery, which allows to pick an image, the image where you can draw shapes for annotations, and the list of annotations.

Injections:

  • Angular Material dialog service and options.
  • gallery service (via IMAGE_GALLERY_SERVICE_KEY).
  • gallery options service.

Properties:

  • image (GalleryImage): the currently edited image, if any.
  • annotations (ListAnnotation<MyAnnotationPayload>): a list of annotations having payload metadata of the specified type (MyAnnotationPayload). This is a mock model provided for demo purposes.

Events:

  • annotationsChange: emitted when annotations change.

The component orchestrates these children components:

  • image annotator toolbar, which allows users to change the current drawing tool. This emits a tool-change event, which is handled by the container component to set the current tool, bound to the image annotator directive.
  • an img element (whose source is image.uri) decorated with the image annotator directive, which wires all the Annotorious editing events to handlers in the container component.
  • a custom image annotations list, bound to the currently edited image, the annotator instance, and the editor component type (editorComponent). This emits a listInit event when the list is initialized.

Custom Image Annotation List

The list of annotations is provided by a custom component. Typically you derive it from ImgAnnotationListComponent<T> where T is the type of the annotation payload (your own metadata model for the annotation). You should wire to the container component its input properties:

  • annotator: the instance of the annotator object as received from Annotorious.
  • editorComponent: the component used to edit a ListAnnotation. Pass the component class, e.g. [editorComponent]="MyEditorComponent".
  • image the image (of type GalleryImage) to be annotated.
  • optionally, you can set the annotationToString property to provide your own function used to build a string from a list annotation object, summarizing its content appropriately.

The base class manages an inner list (of type ImgAnnotationList), which is lazily instantiated when these properties are set.

Example:

// my-img-annotation-list.component.ts

@Component({
  standalone: true,
  selector: 'app-my-img-annotation-list',
  templateUrl: './my-img-annotation-list.component.html',
  styleUrls: ['./my-img-annotation-list.component.css'],
  imports: [CommonModule, MatButtonModule, MatIconModule, ObjectToStringPipe],
})
export class MyImgAnnotationListComponent extends ImgAnnotationListComponent<any> {
  public selectAnnotation(annotation: any): void {
    this.list?.selectAnnotation(annotation);
  }

  public removeAnnotation(index: number): void {
    this.list?.removeAnnotation(index);
  }

  public editAnnotation(annotation: any): void {
    this.list?.editAnnotation(annotation);
  }
}

Its HTML template should display a list bound to list.annotations$, typically with their related actions (select, edit, remove).

Services

A gallery image is any object implementing the GalleryImage interface, which provides the barely minimum properties of each image: a string ID, a URI, a title, and a short description.

Related services are:

  • gallery service, with its injection token. You will provide your own real-world service, which might draw images from IIIF services, cloud services, etc.
  • gallery options service, used as the singleton holding options for the gallery. Options are equal to or derive from GalleryOptions. You can access the options and change them by injecting this service.
  • a mock gallery service implementation, using an online mock images service. This is used during development as a convenient placeholder instead of your custom service.
  • lorem ipsum service, used by the mock gallery service to generate image descriptions.

A gallery service is any service implementing the GalleryService interface, having just two methods: one to retrieve a page of images, and another to retrieve a single image.

In both cases, you can specify image options (first of all the size) via options, which implement interface GalleryOptions. The service is stateless, except for the fact that it caches the options when first used, and reacts to their change from usage to usage.

Also, the list of images in the gallery can be filtered using an implementation of GalleryFilter. This is just a set of key/value pairs, both represented by strings. Usually you will draw a set of keys to show in the filter UI from some thesaurus.

For instance, to filter images by title you will set a filter property with key=title and value equal to any portion of the title to match.

Components

  • gallery filter (cadmus-gallery-filter), to define any number of name=value pairs representing filters applied to the gallery images. The filter gets the gallery list repository injected, so it directly invokes its methods.

    • ➡️ entries: ThesaurusEntry[] | undefined: the entries used to represent image gallery metadata filters. Each entry is a metadatum, with ID=metadatum name and value=label. If not set, users will be allowed to freely type a name rather than picking it from a list.
  • gallery images list (cadmus-gallery-list), to browse a list of images via a gallery service implementation. The type of implementation used is defined by your consumer application (via DI as specified in app.module or appConfig providers). Both the list and its filter depend on the same instance of a gallery list repository (GalleryListRepository), which provides paged data to the UI.

    • ➡️ entries: ThesaurusEntry[] | undefined: the entries used to represent image gallery metadata filters. Each entry is a metadatum, with ID=metadatum name and value=label. If not set, users will be allowed to freely type a name rather than picking it from a list.
    • ⬅️ imagePick: EventEmitter<GalleryImage>
  • gallery image annotator, to annotate an image picked from a gallery by drawing rectangular or polygonal regions on it.

Setup

To use this brick in your Cadmus frontend app:

(1) install packages:

npm i @recogito/annotorious @myrmidon/cadmus-img-annotator @myrmidon/cadmus-img-gallery

(2) ensure to add Annotorious CSS styles to your angular.json like:

"styles": [
  "node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
  "node_modules/@recogito/annotorious/dist/annotorious.min.css",
  "src/styles.css"
]

(3) import modules in your app.module.ts (or app-config.ts) and inject the required image service and default options (you can then change them later via GalleryOptionsService):

// app.module.ts

import { CadmusImgAnnotatorModule } from '@myrmidon/cadmus-img-annotator';
import { CadmusImgGalleryModule } from '@myrmidon/cadmus-img-gallery';

@NgModule({
  // ...
  imports: [
    // ...
    CadmusImgAnnotatorModule,
    CadmusImgGalleryModule,
  ],
  providers: [
    // image gallery: TODO replace with your own
    {
      provide: IMAGE_GALLERY_SERVICE_KEY,
      useClass: MockGalleryService,
    },
    {
      provide: IMAGE_GALLERY_OPTIONS_KEY,
      useValue: {
        baseUri: '',
        count: 50,
        width: 300,
        height: 400,
      },
    },
  ]
  // ...
})
export class AppModule {}

(4) if you want to lookup annotations by their EID, be sure to include the corresponding lookup definition like:

import { IndexLookupDefinitions } from '@myrmidon/cadmus-core';
import {
  GALLERY_IMAGE_ANNOTATIONS_PART_TYPEID,
} from '@myrmidon/cadmus-part-general-ui';

export const INDEX_LOOKUP_DEFINITIONS: IndexLookupDefinitions = {
  // ...
  // gallery
  img_anno_eid: {
    typeId: GALLERY_IMAGE_ANNOTATIONS_PART_TYPEID,
    name: 'eid',
  },
};