Skip to content

Latest commit

 

History

History

counter-style

@jsamr/counter-style

npm semver codecov CI DL/month

A slim CSS Counter Styles Level 3 compliant library with 47 presets including
Arabic, Persian, Thai, Hebrew, Roman, Katana...

The core is less than 1.7kB minified and gzipped.
Each preset is distributed as a separate module.
Available in both CommonJS and ECMAScript modules.
Targets ECMAScript 2015.
Optimized for metro (React Native) and Webpack bundlers.
Based on prior work from Whang Shuwei.


Install

npm add --save @jsamr/counter-style
yarn add @jsamr/counter-style

Using presets

This library exports 47 presets. Find your preset here. Each preset is accessible in a separate module to limit bundle size.

import arabicIndic from '@jsamr/counter-style/presets/arabicIndic';

expect(arabicIndic.renderMarker(78)).toBe('٧٨. ');

PRs are welcomed to support other presets. Very easy to implement thanks to this W3C resource.

The API follows closely the specs for CSS @counter-style at rule. The default export (CounterStyle) is a static object with methods to build CounterStyleRenderer.

Example 1: a lower Russian alphabet renderer

In the below example, we're using the alphabetic counter system and alphabeticFromUnicodeRange builder which allows to specify a contiguous unicode range. For non-contiguous ranges, use the alphabetic builder.

import CounterStyle from '@jsamr/counter-style';

const lowerRussian = CounterStyle.alphabeticFromUnicodeRange(
  0x430, // а
  28
).withSuffix(') ');

// Expect comes from jest testing framework.
// Just a showcase of expected returned values.
expect(lowerRussian.renderCounter(1)).toBe('а');
expect(lowerRussian.renderMarker(1)).toBe('а) ');
expect(lowerRussian.renderCounter(2)).toBe('б');
expect(lowerRussian.renderCounter(3)).toBe('в');
expect(lowerRussian.renderMarker(4)).toBe('г) ');
expect(lowerRussian.renderMarker(5)).toBe('д) ');
expect(lowerRussian.renderCounter(29)).toBe('аа');
expect(lowerRussian.maxMarkerLenInRange(1, 5)).toBe(3);
expect(lowerRussian.maxCounterLenInRange(1, 5)).toBe(1);

Reference: W3C Ready-made Counter Styles: Cyrillic styles.

Example 2: a "Funky" symbolic renderer

In the below example, we're using the symbolic counter system. Note that withSuffix(null) removes default suffix.

import CounterStyle from '@jsamr/counter-style';

// Default suffix is ". ", as per the specs.
const funky = CounterStyle.symbolic('*', '&').withSuffix(null);

// Expect comes from jest testing framework.
// Just a showcase of expected returned values.
expect(funky.renderMarker(1)).toBe('*');
expect(funky.renderMarker(2)).toBe('&');
expect(funky.renderMarker(3)).toBe('**');
expect(funky.renderMarker(4)).toBe('&&');
expect(funky.renderMarker(5)).toBe('***');
expect(funky.maxMarkerLenInRange(1, 5)).toBe(3);
expect(funky.maxCounterLenInRange(1, 5)).toBe(3);

All renderers can be chained to create variants, such as withSuffix, withPaddingLeft, ... See available methods in the docs.

API reference

The API reference is available here.

Caveats

  • Instead of a normal space character, a non-breaking space is used for default prefixes. This is because this library primary usage is for React Native. On iOS, Text elements get trimmed of normal space characters.
  • In numeric and alphabetic systems , one UTF-16 code unit per symbol must be used. Otherwise, length computation will be erroneous. If you really need multi code units per symbol however, you can still set a custom length computer via withMaxLenComputer.
  • When using padding (withPadding) and negative (withNegative) symbols, one UTF-16 code unit per symbol must be used. Otherwise, length computation will be erroneous.
  • Never use combining characters. Grapheme clusters will be considered distinct characters. Beware that is the case for some emojis, see this SO thread.
  • Don't define incomplete additive systems which have holes in their range coverage. For example, an additive system which has no representation for "1" will not translate odd indexes.

Limitations

  • speakAs hasn't been implemented yet.
  • Only textual symbols are supported. Images are not.