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/gve-snapshot-view

v1.1.6

Published

GVE snapshot view

Downloads

351

Readme

GVE Snapshot View Web Component

This project contains the GVE snapshot view web component, which is a pure Typescript, JS-framework independent component to visualize a GVE snapshot with SVG.

🚀 Quick start:

  1. npm i to install packages.
  2. npm run build to build.
  3. npm run start to start the test page. Here you should paste the JSON code representing a snapshot, and click the button to view it. You can copy an example JSON code from arzdc.json, which is not used by the code but is a resource available to the user for this purpose. You can also use abcxy.json to test for automatic positioning with multiple "layers" as derived from nodes added by operations.

API

This component acts as a black box, receiving a snapshot object of type Snapshot, and rendering it into its svg element, according to the specified options and snapshot data.

  • 📦 install: npm i @myrmidon/gve-snapshot-view
  • 🔑 selector: gve-snapshot-view
  • input:
    • ▶️ data (SnapshotViewData | undefined | null): the snapshot view data to render:
      • snapshot (Snapshot):
        • size (Size): the SVG size.
          • width (number)
          • height (number)
        • style (string | undefined): the SVG style.
        • defs (string | undefined): the optional SVG defs code to be used in the view.
        • image (SnapshotImage | undefined): the optional background image:
          • url (string)
          • canvas (Rectangle): the optional canvas to fit the image into. If not specified, the image will have the same size of the SVG container, and placed at its top-left corner (0,0):
            • x (number)
            • y (number)
            • width (number)
            • height (number)
          • opacity (number): the default background image opacity. If not specified this is equal to 0. So, to show the image you should specify an opacity value here, or set it later via the renderer's setImageOpacity method.
        • text (string | CharNode[]): the base text. This can be a string, which will be converted into an array of nodes, or directly an array of nodes.
          • id (number)
          • index (number)
          • label (string)
          • data (string)
          • sourceTag: (string | undefined)
          • features (Feature[] | undefined):
            • name (string)
            • value (string)
            • setPolicy (number)
        • textStyle (string | undefined): the style for the text g element.
        • textOptions (SvgBaseTextOptions):
          • lineHeightOffset (number): offset to add to each calculated line height.
          • charSpacing (number): offset to add to each calculated character width.
          • spcWidthOffset (number): offset to add to each calculated space width.
          • offset (Point): offset for the text origin.
            • x (number)
            • y (number)
          • minLineHeights (Record<number, number>): minimum line heights for each line number (1-N). You can specify a minimum line height for each line number to ensure that its height is at least the specified value. This is useful for making room between lines for annotations that may exceed the base text.
          • operations (CharChainOperation[]):
            • id (string)
            • rank (number | undefined)
            • groupId (string | undefined)
            • features (OperationFeature[] | undefined):
              • name (string)
              • value (string)
              • setPolicy (number)
              • isNegated (boolean | undefined)
              • isGlobal (boolean | undefined)
              • isShortLived (boolean | undefined)
            • sources (OperationSource[] | undefined)
            • diplomatics (OperationDiplomatics | undefined):
              • g (string)
              • isNewTextHidden (boolean | undefined)
              • features (Feature[] | undefined):
                • name (string)
                • value (string)
                • setPolicy (FeatureSetPolicy)
              • elementFeatures (Record<string, Feature[]>)
            • type (OperationType)
            • at (number)
            • atAsIndex (boolean | undefined)
            • to (number | undefined): to coordinate, for move/swap only.
            • toAsIndex (boolean | undefined)
            • inputTag (string | undefined)
            • outputTag (string | undefined)
            • run (number)
            • toRun (number | undefined)
            • value (string | undefined)
          • opStyle (string): the style for the operations g element.
          • timelines (Record<string, GveAnimationTimeline>):
            • tag (string)
            • tweens (GveAnimationTween[]):
              • label (string)
              • note (string | undefined)
              • type (string);
              • selector (string)
              • vars (string | undefined)
              • vars2 (string | undefined)
              • position (string | undefined)
            • vars (GveAnimationVars | undefined)
      • options (SnapshotViewOptions | undefined):
        • hideBase (boolean | undefined): true to hide the base text.
        • hideOperations (boolean | undefined): true to hide the operations.
        • operationIds (string[] | undefined): the IDs of the operations to show; all the other operations will be hidden by setting their opacity to 0.
        • showRulers (boolean | undefined): true to show rulers.
        • showGrid (boolean | undefined): true to show gridlines on the rulers.
        • rulerColor (boolean | undefined): the rulers and gridlines color (default #aaa).
        • debug (boolean | undefined): true to enable debug mode.
        • delayedRender (boolean | undefined): true to delay the positioning of rendered text elements so that position is calculated after they are rendered in the DOM. This might be useful in some environments like Angular.
        • panZoom (boolean | undefined): true to enable pan and zoom functionality.
  • output:
    • 🔥 snapshotRender: fired when the snapshot is rendered; detail (SnapshotViewRenderEvent) has the root SVG element and the renderer service:
      • svg (SVGSVGElement)
      • renderer (SnapshotViewService):
        • characters (CharNode[])
        • visuals (GveVisual[]):
          • id (string)
          • type (string)
          • style (string | undefined)
          • class (string | undefined)
          • data (any | undefined)
          • element (SVGElement | undefined)
          • attributes (Record<string, string> | undefined): not used
          • handlers (object | undefined)
    • 🔥 visualEvent: fired when the user interacts with any rendered visual with the mouse; detail (SnapshotViewVisualEvent) has the mouse event and the source visual object (GveVisual):
      • event (MouseEvent)
      • source (GveVisual)

GVE-related models are currently defined in this library for convenience (src/models). Once they reach stability, they will be moved into an independent library (gve-snapshot-core) which will thus be shared among a wider range of frontend components dealing with snapshots visualization.

Usage

Example usage:

// getting custom component
let component = document.querySelector('gve-snapshot-view');

// setting input data
component.data = data;

// listening to events
component.addEventListener("snapshotRender", (event) => {
  console.log("Snapshot rendered:", event.detail);
});
component.addEventListener("visualEvent", (event) => {
  console.log("Visual event:", event.detail);
});

The component renders a snapshot in this starting template:

<svg id="snapshot">
  <!-- background image -->
  <image id="image" x="0" y="0" width="0" height="0"></image>
  <!-- rulers -->
  <g id="rulers"></g>
  <!-- base text -->
  <g id="base"></g>
  <!-- operations -->
  <g id="operations"></g>
</svg>

According to data and options, the component will add new SVG elements as descendants of these g elements.

Element g#base contains multiline text, where each character (except for newline) is represented by an SVG text element. In most cases, the characters are displayed one after another on a line (until any newline is found), except for those character having a manually set position (as defined by the x and y features of the source model for each character).

To this end, the text renderer must get the bounding box of each character added and use it to determine the position of the next one. In some environments, like Angular, this does not work because there is some delay (probably due to zone.js) between the addition of the SVG element to the DOM and its effective rendition.

To work around this issue the component has a special rendition mode, enabled by setting its data's delayedRender setting to true. When this happens, the component behaves as follows:

  1. each text element gets added and measured, but usually this measurement ends up with a 0-sized bounding box. Anyway, this will not be an issue here because text opacity is set to 0, which ensures that nothing is displayed at this stage (which would result in overwriting all the characters at the same position). The original opacity value, when set (and when different from 1), is preserved for a later restore.
  2. once all the elements have been added, inside the handler of requestAnimationFrame all the text elements corresponding to character and their visuals are updated after querying each text element's bounding box. Also, the element's opacity is restored.

If instead delayedRender is disabled, the position is set immediately after each SVG text is added, without resetting its opacity to 0, and this already is the correct position.

Services

Snapshot View Service

The snapshot view service is the engine behind the snapshot view component, used to create all its SVG elements except for those belonging to its main template. This is just a POJO class, which must be simply instantiated and used, and maintains the state for the snapshot viewer.

The service can be accessed on rendition, and provides this API:

  • ▶️ characters (CharNode[]): the base text characters of the snapshot.
  • ▶️ visuals (GveVisual[]): the viewmodels of visuals rendered by the service.
  • 🔵 translateSpecialChar: translate an ASCII special character to a printable character.
  • 🔵 stringToBaseChars: convert a string to the character nodes of a base text.
  • 🔵 addEventHandlers: add event listeners to a visual.
  • 🔵 removeEventHandlers: remove event listeners from a visual.
  • 🔵 attachEventHandlers: attach or detach event handlers to the rendered visuals.
  • 🔵 toggleRulers: toggle the rulers on (pass true) or off (pass false), or just toggle the existing state (do not pass anything). Note that if rulers were not rendered by setting the corresponding options, this method will not have any effect.
  • 🔵 setImageOpacity: set the opacity of the background image element, if any.
  • 🔵 render: render the specified snapshot.
  • 🔵 playTimeline: play the specified timeline.
  • 🔵 pauseTimeline: pause the specified timeline.
  • 🔵 resumeTimeline: resume the specified timeline.
  • 🔵 reverseTimeline: play the specified timeline in reverse.

The main method in the service is render, which gets:

  • the snapshot (Snapshot) to render;
  • the rendering options (SnapshotViewOptions);
  • the optional event handlers (GveVisualEventHandlers) for the rendered visuals.

The service maintains its rendition in these SVG elements:

  • the root SVG svg element. This is injected in the service constructor, and it's assumed to have children elements for image, rulers, base, and operations, with their corresponding identifiers.
  • a background SVG image element with ID image.
  • the rulers SVG g element with ID rulers.
  • the base text SVG g element with ID base.
  • the operations root element with ID operations.

It also maintains its logical state in:

  • an array of CharNode items (characters property): the viewmodels of the rendered characters.
  • an array of GveVisual items (visuals property): the viewmodels of the rendered visuals.

The rendition is as follows:

  1. set the attributes related to the SVG container as a whole:

    • width and height.
    • style.
    • background image with its X, Y, width, height.
    • base text style for the text root element.
    • operations style for the operations root element.
  2. if there are any defs defined, add them inside a newly created defs element, then appended to the svg element.

  3. if required, create the rulers by adding SVG elements to the rulers g group. You can later toggle rulers off and on using toggleRulers, which just changes the rulers g element display style accordingly. So if you want rulers, specify this in the options or they will not be created and toggle will have no effect.

  4. render the base text (delayed if requested): this gets the characters from the snapshot (or builds them if the snapshot text is just a string rather than an array of CharNode's ), and renders them on 1 or more lines (every \n character ends a line). If the character has x and y among its features, these will be used as its coordinates, instead of the automatically calculated ones. Each character produces:

    • an SVG text appended to the text g element, with attributes id=c_ + character ID (=its ordinal number), x, y, dominant-baseline=hanging; if using delayed rendering, the text is hidden with opacity=0 to be shown later in the rendition process. If the character node has features for style and classes, the corresponding attributes are added too.
    • a visual of type GveTextVisual added to the visuals property. This has ID=c_<ID>, and contains x, y, type=char, width, height, lineNr=current line number, data=the source CharNode object, and element=the SVG text element created for the character. Optionally it has style and class if set from features. If requested, the visual also gets event listeners added.
  5. render operation visuals: each operation having a visual rendition produces:

    • a new g element as defined by the operation SVG g model. This element is added to the operations group element. The operation element also gets an opacity=0 attribute if requested (you can specify a list of transparent operations via the snapshot view transparentIds option).
    • a visual of type GveVisual added to the visuals property. This has ID = operation's ID, and contains x, y, width, height, type=g, data=the source CharChainOperation object, and element=the SVG g element created for the operation. Optionally it has style and class if set from features. If requested, the visual also gets event listeners added.
  6. if delayed rendering is enabled, reposition text elements after measuring them and make them visible.

  7. add pan and zoom support if requested.

Popup Service

You can use the PopupService to open/close a popup. In this case, you will need to include the following CSS:

.popup {
  position: fixed;
  top: 0;
  left: 0;
  background-color: white;
  padding: 10px;
  box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
  z-index: 10;
}
@keyframes openPopup {
  0% {
    opacity: 0;
    transform: scale(0.5);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes closePopup {
  0% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(0.5);
  }
}
.popup-open {
  animation: openPopup 0.3s forwards;
}
.popup-close {
  animation: closePopup 0.3s forwards;
}

ParsedXmlTag

This utility class represents an XML tag as parsed from a string. It can be used to manage its attributes (read, add, or update them), which can be useful when manipulating SVG code.

  • 🔄 name (string): the tag name.
  • 🔄 type (string): the tag type (open, close, empty).
  • 🔄 index: the index of the first character of the tag (<) in the source XML string.
  • 🔄 length: the length of the tag in the source XML string, starting from index.
  • 🔄 attributes: the attributes in the tag:
    • 🔄 name (string): the name.
    • 🔄 value (string): the value.
    • 🔄 index: the index of the first character of the attribute's name in the source XML string.
    • 🔄 length: the length of the attribute in the source XML string, starting from its name and including its value if any.
    • 🔄 singleQuote: true if the attribute value was wrapped in single quotes rather than in double quotes in the source XML string.
  • 🔵 parse (static): parse an XML tag from a string at the specified index.
  • 🔵 toString: build an XML tag from the specified tag information, overriding the specified attributes values.

Workspace Template

These are the steps for building the template for a pure JS library in VSCode using Typescript, JEST testing, code maps, and webpack bundling like that used here:

  1. create a new folder.

  2. enter it and run npm init.

  3. install these packages:

    npm i --save-dev source-map-loader ts-loader lite-server concurrency
    npm i --save-dev jest ts-jest @types/jest
    npm i --save-dev rollup rollup-plugin-terser @rollup/plugin-typescript @rollup/plugin-commonjs @rollup/plugin-node-resolve --force
  4. change package.json to add the scripts, exports, main, dependencies, and types, like in the portion of its code sampled here:

    {
      "main": "./dist/index.js",
      "type": "module",
      "types": "./dist/index.d.ts",
      "scripts": {
        "build": "rollup -c",
        "watch": "rollup -c -w",
        "start": "concurrently \"rollup -c -w\" \"lite-server\"",
        "test": "jest"
      },
      "exports": {
        ".": "./dist/index.js"
      },
      "dependencies": {
        "typescript": "^5.4.5"
      }
    }
  5. add tsconfig.json for TS configuration:

    {
      "compilerOptions": {
        "target": "es6",
        "module": "es6",
        "moduleResolution": "node",
        "lib": ["dom", "es2015"],
        "declaration": true,
        "outDir": "./dist",
        "strict": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true
      },
      "include": ["src"],
      "exclude": ["node_modules", "dist"]
    }
  6. add rollup.config.mjs for bundling:

    import commonjs from "@rollup/plugin-commonjs";
    import typescript from "@rollup/plugin-typescript";
    import { terser } from "rollup-plugin-terser"; // Optional: Minification (for production)
    import { nodeResolve } from "@rollup/plugin-node-resolve";
    
    export default {
      input: "src/index.ts", // Entry point for your library
      output: [
        {
          file: "dist/index.js", // Output file for ES6 modules
          format: "es",
        },
        {
          file: "dist/index.cjs.min.js", // Output file for CommonJS (minified)
          format: "cjs",
          sourcemap: true,
          plugins: [terser()], // Minify for production
        },
      ],
      plugins: [
        typescript({ tsconfig: "./tsconfig.json" }), // Use your existing tsconfig.json
        commonjs(), // Convert CommonJS modules to ES6 (for GSAP),
        nodeResolve(), // Resolve node_modules
      ],
    };
  7. add jest.config.js for testing:

    module.exports = {
      preset: "ts-jest",
      testEnvironment: "node",
    };
  8. create in .vscode launch.json for debugging:

    {
      "version": "0.2.0",
      "configurations": [
        {
          "type": "chrome",
          "request": "launch",
          "name": "Launch Chrome against localhost",
          "url": "http://localhost:3000",
          "webRoot": "${workspaceFolder}"
        }
      ]
    }
  9. add index.html and its consumer.js script to the root to host your controls for testing. This should:

    • load your bundled library: <script type="module" src="./dist/bundle.js"></script>;
    • load the test consumer code: <script type="module" src="consumer.js"></script>;
    • use the web component by just placing its element in the page.
    • the test JS code must be inside document.addEventListener("DOMContentLoaded", (event) => { }); to allow for the web component to load before interacting with it.
  10. add your code (services and components) under src.

  11. export all the required objects from the src/index.ts API entrypoint.

  12. add .npmignore with a content like this:

    node_modules
    src
    *.md
    *.test.ts
    *.spec.ts
    *.spec.d.ts
    tsconfig.json
    package-lock.json
    *.log
    *.env
    .vscode
    consumer.js
    index.html
    jest.config.cjs
    rollup.config.mjs

⚠️ Adding this is essential for NPM, otherwise it will use .gitignore for creating a package, which will result in an unusable package including just the sources, while we want the inverse. You can use npm pack to create a .tz file with the package to check its content before publishing.

You can now use:

  • npm run build to build.
  • npm run start to launch the server with the test page.
  • npm run watch to start and watch for changes.
  • npm publish to publish the package. Be sure to update the version in package.json as required before publishing.

Note on Lite Server

The lite-server does not automatically rebuild your TypeScript files when they change. It only refreshes the browser when HTML or JavaScript files change.

To have your TypeScript files automatically recompile when you make changes, you can use tsc --watch command. This command starts the TypeScript compiler in watch mode; the compiler watches for file changes and recompiles when it sees them.

Then, you can run npm run watch in a separate terminal to start the TypeScript compiler in watch mode.

However, this will not refresh your browser when your TypeScript files are recompiled. To do this, you can use a tool like concurrently to run both lite-server and tsc --watch at the same time, and have lite-server refresh the browser whenever your compiled JavaScript files change.

Once installed this Update your start script to run both commands:

"scripts": {
  "start": "concurrently \"tsc --watch\" \"lite-server\""
}

Now, when you run npm start, it will start both the TypeScript compiler in watch mode and lite-server. When you make changes to your TypeScript files, they will be automatically recompiled, and lite-server will refresh your browser.