@jsamr/counter-style
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.
Creating custom style renderers
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. - Chinese renderers haven't been implemented, requiring custom logic. PRs welcomed!
- Only textual symbols are supported. Images are not.