@orianatech/pire 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/NOTICE +19 -0
  2. package/README.md +43 -5
  3. package/dist/components/core/Icon.d.cts +22 -7
  4. package/dist/components/core/Icon.d.ts +22 -7
  5. package/dist/components/core/Icon.d.ts.map +1 -1
  6. package/dist/components/core/icon-data.generated.d.cts +1 -0
  7. package/dist/components/core/icon-data.generated.d.ts +2 -0
  8. package/dist/components/core/icon-data.generated.d.ts.map +1 -0
  9. package/dist/components/core/icon-map.d.cts +13 -0
  10. package/dist/components/core/icon-map.d.ts +14 -0
  11. package/dist/components/core/icon-map.d.ts.map +1 -0
  12. package/dist/components/navigation/ShellBar.d.cts +8 -1
  13. package/dist/components/navigation/ShellBar.d.ts +8 -1
  14. package/dist/components/navigation/ShellBar.d.ts.map +1 -1
  15. package/dist/components/navigation/SideNav.d.cts +56 -4
  16. package/dist/components/navigation/SideNav.d.ts +56 -4
  17. package/dist/components/navigation/SideNav.d.ts.map +1 -1
  18. package/dist/components.css +22 -0
  19. package/dist/fonts/LICENSE-jetbrains-mono.txt +93 -0
  20. package/dist/fonts/LICENSE-public-sans.txt +93 -0
  21. package/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  22. package/dist/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  23. package/dist/fonts/jetbrains-mono-latin-600-normal.woff2 +0 -0
  24. package/dist/fonts/public-sans-latin-400-normal.woff2 +0 -0
  25. package/dist/fonts/public-sans-latin-500-normal.woff2 +0 -0
  26. package/dist/fonts/public-sans-latin-600-normal.woff2 +0 -0
  27. package/dist/fonts/public-sans-latin-700-normal.woff2 +0 -0
  28. package/dist/index.cjs +276 -81
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +277 -82
  34. package/dist/index.js.map +1 -1
  35. package/dist/tokens/fonts.css +20 -15
  36. package/package.json +8 -3
package/NOTICE CHANGED
@@ -5,3 +5,22 @@ This product includes software developed at Oriana Tech.
5
5
 
6
6
  Licensed under the Apache License, Version 2.0. You may obtain a copy of the
7
7
  License at http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ --------------------------------------------------------------------------------
10
+ THIRD-PARTY FONT SOFTWARE
11
+
12
+ This package redistributes the font binaries under dist/fonts/. They are not
13
+ covered by the Apache License above; each is licensed under the SIL Open Font
14
+ License, Version 1.1, whose full text ships alongside them.
15
+
16
+ Public Sans
17
+ Copyright (c) 2015 Impallari Type; Copyright (c) 2019 United States Government
18
+ SIL Open Font License 1.1 - dist/fonts/LICENSE-public-sans.txt
19
+
20
+ JetBrains Mono
21
+ Copyright (c) 2020 JetBrains s.r.o.
22
+ SIL Open Font License 1.1 - dist/fonts/LICENSE-jetbrains-mono.txt
23
+
24
+ Both are redistributed unmodified. They are aliased only at the CSS
25
+ `font-family` level, to "Pire Sans" and "Pire Mono"; the binaries themselves are
26
+ untouched, so the Reserved Font Name clause is not engaged.
package/README.md CHANGED
@@ -10,7 +10,8 @@ Requires Node 20+ and pnpm.
10
10
  ```bash
11
11
  pnpm install
12
12
  pnpm storybook # docs + playground at http://localhost:6006
13
- pnpm build # dist/ (ESM + CJS + .d.ts + CSS)
13
+ pnpm build # dist/ (ESM + CJS + .d.ts + CSS), icon codegen first
14
+ pnpm icons:generate # re-inline the Carbon glyphs listed in icon-map.ts
14
15
  pnpm typecheck # tsc --noEmit
15
16
  pnpm lint # biome
16
17
  ```
@@ -76,14 +77,45 @@ Override tokens, never component rules:
76
77
 
77
78
  ### Icons
78
79
 
79
- Names are Lucide slugs; the shipped pack is IBM Carbon, resolved through an internal map. Glyphs
80
- load from a CDN by default self-host in production:
80
+ **Zero configuration, and no network.** Every glyph is inlined into the bundle at build time and
81
+ rendered as inline SVG, so icons paint offline, cost no requests, and report nothing to anyone.
82
+
83
+ ```tsx
84
+ <Icon name="shopping-cart" size={20} /> {/* decorative: aria-hidden */}
85
+ <Icon name="lock" size={20} label="Locked record" /> {/* meaningful: role="img" + aria-label */}
86
+ ```
87
+
88
+ Names are canonical **Lucide slugs**; the shipped pack is IBM Carbon, reached through an internal
89
+ map, so a pack swap never touches a call site. The same names are what the `icon` prop on `Button`,
90
+ `IconButton`, `Card`, `KpiTile`, `Tabs`, `EmptyState` and `InlineMessage` takes.
91
+
92
+ Glyphs are filled with `currentColor`, so an icon takes the colour of the text it sits in — style the
93
+ container, not the icon. `size` is the pixel box: 16 in dense UI, 20 in headers, 24 in tiles.
94
+
95
+ An unrecognised name renders an empty box of the right size and logs one `[pire]` console warning
96
+ naming it. The warning is compiled out of production builds.
97
+
98
+ The bundled set covers what the ERP screens use. For anything else, register the *inner* markup of a
99
+ 32×32 SVG once at startup:
81
100
 
82
101
  ```ts
83
102
  import { configureIcons } from '@orianatech/pire';
84
- configureIcons({ base: '/assets/icons/' }); // folder of <slug>.svg
103
+
104
+ configureIcons({
105
+ resolve: (name) => (name === 'oriana-mark' ? '<circle cx="16" cy="16" r="8"/>' : undefined),
106
+ });
85
107
  ```
86
108
 
109
+ > **Upgrading from 0.3 or earlier.** Glyphs used to be fetched per render from `cdn.jsdelivr.net`,
110
+ > which meant blank icons in an offline PWA, so apps self-hosted the SVGs and called
111
+ > `configureIcons({ base })`. That is now unnecessary: **delete the `configureIcons({ base })` call
112
+ > and the vendored `<slug>.svg` files.** `base` and `pack` are still accepted so existing code keeps
113
+ > compiling, but they are ignored and warn in development. The `lucide` pack is gone —
114
+ > `pack="lucide"` now renders the Carbon glyph.
115
+
116
+ Adding a glyph to the library itself is one line in `src/components/core/icon-map.ts` plus a build;
117
+ see the Icons section of `CLAUDE.md`.
118
+
87
119
  ## Migrating from the HTML/JSX prototypes
88
120
 
89
121
  | Prototype API | Package API |
@@ -104,7 +136,13 @@ configureIcons({ base: '/assets/icons/' }); // folder of <slug>.svg
104
136
 
105
137
  ## Toolchain notes
106
138
 
107
- - `pnpm build` runs `tsup` for the JS bundles, then `tsc -p tsconfig.build.json` for the
139
+ - `pnpm build` first runs `scripts/generate-icon-data.mjs`, which inlines the Carbon SVGs listed in
140
+ `src/components/core/icon-map.ts` into a committed `icon-data.generated.ts`. `@carbon/icons` is a
141
+ **devDependency on purpose** — it depends on `@ibm/telemetry-js` and runs `ibmtelemetry` from a
142
+ `postinstall` script, which as a runtime dependency would execute in every app that installs Pire.
143
+ The generated file is committed because typecheck, lint, tests and Storybook all read `src/`
144
+ without building; its install script is denied in `pnpm-workspace.yaml`.
145
+ - `pnpm build` then runs `tsup` for the JS bundles, then `tsc -p tsconfig.build.json` for the
108
146
  `.d.ts` files, then `scripts/postbuild-types.mjs`. tsup's own declaration pipeline is not
109
147
  compatible with the TypeScript 7 native compiler, so `dts` is off in `tsup.config.ts` —
110
148
  leave it that way. The post-pass makes the emitted declarations resolvable under
@@ -1,22 +1,37 @@
1
- import * as React from 'react';
1
+ import type * as React from 'react';
2
+ /** @deprecated Retained only so existing `pack` values still typecheck. Glyphs are bundled from
3
+ * the Carbon pack; there is no second pack and no remote fetch, so the value is ignored. */
2
4
  export type IconPack = 'carbon' | 'lucide';
3
5
  export interface IconConfig {
4
- /** Pack used when an <Icon> does not specify one. Default 'carbon'. */
6
+ /** @deprecated Ignored. The bundled Carbon pack is the only pack `lucide` was a remote-URL
7
+ * pack and no longer exists. Setting this warns in development. */
5
8
  pack?: IconPack;
6
- /** Serve glyphs from your own origin: a folder of <slug>.svg files. Recommended in production. */
9
+ /** @deprecated Ignored — glyphs are inlined in the bundle, so there is nothing to serve from a
10
+ * folder. Apps that vendored `<slug>.svg` files into `public/` to survive offline can delete
11
+ * both the files and this call. Setting it warns in development. */
7
12
  base?: string;
13
+ /** Escape hatch for a glyph Pire does not bundle, or to override one. Return the *inner* markup
14
+ * of a 32x32 SVG (`'<path d="…"/>'`), or `undefined` to fall through to the bundled set. The
15
+ * string is injected as-is, so it must be authored markup, never user input. */
16
+ resolve?: (name: string) => string | undefined;
8
17
  }
9
- /** Point the whole app at a self-hosted glyph folder, or switch packs, once at startup. */
18
+ /** Optional. The bundled Carbon set needs no configuration this exists only to register glyphs
19
+ * Pire does not ship, once at startup. */
10
20
  export declare function configureIcons(next: IconConfig): void;
11
21
  export interface IconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
12
- /** Canonical (Lucide) slug, e.g. "shopping-cart". Mapped automatically for the Carbon pack. */
22
+ /** Canonical (Lucide) slug, e.g. "shopping-cart". Translated to the Carbon pack automatically. */
13
23
  name: string;
14
24
  /** Pixel box. 16 in dense UI, 20 in headers, 24 in tiles. */
15
25
  size?: number;
16
26
  /** Accessible name. Omit for decorative icons — they are hidden from assistive tech. */
17
27
  label?: string;
28
+ /** @deprecated Ignored. See {@link IconConfig.pack}. */
18
29
  pack?: IconPack;
19
30
  }
20
- /** Glyph painted as a CSS mask over currentColor, so it inherits text colour everywhere.
21
- * A missing glyph degrades to empty space rather than a solid block. */
31
+ /** Glyph rendered as inline SVG filled with `currentColor`, so it inherits text colour everywhere
32
+ * and paints with no network at all which the previous CDN-backed implementation did not, leaving
33
+ * every icon blank offline in the PWAs that consume this library.
34
+ *
35
+ * An unknown name still renders nothing, so a typo cannot collapse a layout, but it warns in
36
+ * development: the silent blank gap was the failure mode worth killing. */
22
37
  export declare function Icon({ name, size, label, pack, style, ...rest }: IconProps): React.JSX.Element;
@@ -1,23 +1,38 @@
1
- import * as React from 'react';
1
+ import type * as React from 'react';
2
+ /** @deprecated Retained only so existing `pack` values still typecheck. Glyphs are bundled from
3
+ * the Carbon pack; there is no second pack and no remote fetch, so the value is ignored. */
2
4
  export type IconPack = 'carbon' | 'lucide';
3
5
  export interface IconConfig {
4
- /** Pack used when an <Icon> does not specify one. Default 'carbon'. */
6
+ /** @deprecated Ignored. The bundled Carbon pack is the only pack `lucide` was a remote-URL
7
+ * pack and no longer exists. Setting this warns in development. */
5
8
  pack?: IconPack;
6
- /** Serve glyphs from your own origin: a folder of <slug>.svg files. Recommended in production. */
9
+ /** @deprecated Ignored — glyphs are inlined in the bundle, so there is nothing to serve from a
10
+ * folder. Apps that vendored `<slug>.svg` files into `public/` to survive offline can delete
11
+ * both the files and this call. Setting it warns in development. */
7
12
  base?: string;
13
+ /** Escape hatch for a glyph Pire does not bundle, or to override one. Return the *inner* markup
14
+ * of a 32x32 SVG (`'<path d="…"/>'`), or `undefined` to fall through to the bundled set. The
15
+ * string is injected as-is, so it must be authored markup, never user input. */
16
+ resolve?: (name: string) => string | undefined;
8
17
  }
9
- /** Point the whole app at a self-hosted glyph folder, or switch packs, once at startup. */
18
+ /** Optional. The bundled Carbon set needs no configuration this exists only to register glyphs
19
+ * Pire does not ship, once at startup. */
10
20
  export declare function configureIcons(next: IconConfig): void;
11
21
  export interface IconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
12
- /** Canonical (Lucide) slug, e.g. "shopping-cart". Mapped automatically for the Carbon pack. */
22
+ /** Canonical (Lucide) slug, e.g. "shopping-cart". Translated to the Carbon pack automatically. */
13
23
  name: string;
14
24
  /** Pixel box. 16 in dense UI, 20 in headers, 24 in tiles. */
15
25
  size?: number;
16
26
  /** Accessible name. Omit for decorative icons — they are hidden from assistive tech. */
17
27
  label?: string;
28
+ /** @deprecated Ignored. See {@link IconConfig.pack}. */
18
29
  pack?: IconPack;
19
30
  }
20
- /** Glyph painted as a CSS mask over currentColor, so it inherits text colour everywhere.
21
- * A missing glyph degrades to empty space rather than a solid block. */
31
+ /** Glyph rendered as inline SVG filled with `currentColor`, so it inherits text colour everywhere
32
+ * and paints with no network at all which the previous CDN-backed implementation did not, leaving
33
+ * every icon blank offline in the PWAs that consume this library.
34
+ *
35
+ * An unknown name still renders nothing, so a typo cannot collapse a layout, but it warns in
36
+ * development: the silent blank gap was the failure mode worth killing. */
22
37
  export declare function Icon({ name, size, label, pack, style, ...rest }: IconProps): React.JSX.Element;
23
38
  //# sourceMappingURL=Icon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../../../src/components/core/Icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AA4B3C,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,QAAsC;AAErF,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC;IACxF,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED;yEACyE;AACzE,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,IAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,qBAuB/E"}
1
+ {"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../../../src/components/core/Icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AA2BpC;6FAC6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB;wEACoE;IACpE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;yEAEqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;qFAEiF;IACjF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAChD;AAID;2CAC2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAerD;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC;IACxF,kGAAkG;IAClG,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED;;;;;4EAK4E;AAC5E,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,IAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,qBAqC/E"}
@@ -0,0 +1 @@
1
+ export declare const ICON_MARKUP: Record<string, string>;
@@ -0,0 +1,2 @@
1
+ export declare const ICON_MARKUP: Record<string, string>;
2
+ //# sourceMappingURL=icon-data.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-data.generated.d.ts","sourceRoot":"","sources":["../../../src/components/core/icon-data.generated.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgE9C,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Canonical Pire icon names are Lucide slugs; the shipped IBM Carbon pack is reached through
2
+ * this map, so swapping packs never touches a call site. A new icon name used by a component
3
+ * must be added here — that is the whole registration step.
4
+ *
5
+ * This file is also the single source of truth for *which glyphs get vendored*:
6
+ * `scripts/generate-icon-data.mjs` reads it and inlines the matching SVG from
7
+ * `@carbon/icons` into `icon-data.generated.ts`. The script cannot import TypeScript, so it
8
+ * parses this object textually. **Every entry must stay a literal `'name': 'slug',` pair.**
9
+ * A spread, a computed key or a template literal is invisible to the generator: the glyph
10
+ * never gets vendored and `Icon` warns at runtime instead of rendering. Multiple names may
11
+ * point at one slug (`boxes` and `package` both mean `box`); the generator de-duplicates.
12
+ */
13
+ export declare const CARBON_MAP: Record<string, string>;
@@ -0,0 +1,14 @@
1
+ /** Canonical Pire icon names are Lucide slugs; the shipped IBM Carbon pack is reached through
2
+ * this map, so swapping packs never touches a call site. A new icon name used by a component
3
+ * must be added here — that is the whole registration step.
4
+ *
5
+ * This file is also the single source of truth for *which glyphs get vendored*:
6
+ * `scripts/generate-icon-data.mjs` reads it and inlines the matching SVG from
7
+ * `@carbon/icons` into `icon-data.generated.ts`. The script cannot import TypeScript, so it
8
+ * parses this object textually. **Every entry must stay a literal `'name': 'slug',` pair.**
9
+ * A spread, a computed key or a template literal is invisible to the generator: the glyph
10
+ * never gets vendored and `Icon` warns at runtime instead of rendering. Multiple names may
11
+ * point at one slug (`boxes` and `package` both mean `box`); the generator de-duplicates.
12
+ */
13
+ export declare const CARBON_MAP: Record<string, string>;
14
+ //# sourceMappingURL=icon-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-map.d.ts","sourceRoot":"","sources":["../../../src/components/core/icon-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiB7C,CAAC"}
@@ -8,10 +8,17 @@ export interface ShellBarProps extends Omit<React.HTMLAttributes<HTMLElement>, '
8
8
  title?: React.ReactNode;
9
9
  onMenu?: () => void;
10
10
  onSearch?: () => void;
11
+ /** Accessible name for the menu button. Defaults to English, and the bar has no access to the app's
12
+ * catalogue — a Spanish product must pass its own or ship an English screen-reader label. */
13
+ menuLabel?: string;
14
+ /** Accessible name for the search button, for the same reason as {@link ShellBarProps.menuLabel}.
15
+ * Until this existed the only way to localise it was to drop `onSearch` and re-render the icon by
16
+ * hand, which is how a shell ends up with two search icons. */
17
+ searchLabel?: string;
11
18
  /** IconButtons with variant="inverse". */
12
19
  actions?: React.ReactNode;
13
20
  /** Full name — initials render into the avatar. */
14
21
  user?: string;
15
22
  }
16
23
  /** Fixed 48px application chrome. One per page, always the first landmark. */
17
- export declare function ShellBar({ product, monogram, title, onMenu, onSearch, actions, user, className, ...rest }: ShellBarProps): React.JSX.Element;
24
+ export declare function ShellBar({ product, monogram, title, onMenu, onSearch, menuLabel, searchLabel, actions, user, className, ...rest }: ShellBarProps): React.JSX.Element;
@@ -8,11 +8,18 @@ export interface ShellBarProps extends Omit<React.HTMLAttributes<HTMLElement>, '
8
8
  title?: React.ReactNode;
9
9
  onMenu?: () => void;
10
10
  onSearch?: () => void;
11
+ /** Accessible name for the menu button. Defaults to English, and the bar has no access to the app's
12
+ * catalogue — a Spanish product must pass its own or ship an English screen-reader label. */
13
+ menuLabel?: string;
14
+ /** Accessible name for the search button, for the same reason as {@link ShellBarProps.menuLabel}.
15
+ * Until this existed the only way to localise it was to drop `onSearch` and re-render the icon by
16
+ * hand, which is how a shell ends up with two search icons. */
17
+ searchLabel?: string;
11
18
  /** IconButtons with variant="inverse". */
12
19
  actions?: React.ReactNode;
13
20
  /** Full name — initials render into the avatar. */
14
21
  user?: string;
15
22
  }
16
23
  /** Fixed 48px application chrome. One per page, always the first landmark. */
17
- export declare function ShellBar({ product, monogram, title, onMenu, onSearch, actions, user, className, ...rest }: ShellBarProps): React.JSX.Element;
24
+ export declare function ShellBar({ product, monogram, title, onMenu, onSearch, menuLabel, searchLabel, actions, user, className, ...rest }: ShellBarProps): React.JSX.Element;
18
25
  //# sourceMappingURL=ShellBar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShellBar.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/ShellBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IACrF,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,qBAaxH"}
1
+ {"version":3,"file":"ShellBar.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/ShellBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IACrF,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;kGAC8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;oEAEgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAA6B,EAClG,WAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,qBAa3E"}
@@ -1,9 +1,24 @@
1
- import type * as React from 'react';
1
+ import * as React from 'react';
2
+ /** A second-level destination, nested under a first-level entry.
3
+ *
4
+ * Deliberately has no `icon`: only the first level carries one, so the second level reads as an
5
+ * indented list of labels under a single glyph rather than a wall of decoration. Leaving `icon` off
6
+ * the *type* is what stops that drifting back in one call site at a time. */
7
+ export interface SideNavChildItem {
8
+ id: string;
9
+ label: string;
10
+ count?: number;
11
+ }
2
12
  export interface SideNavItem {
3
13
  id: string;
4
14
  label: string;
5
15
  icon?: string;
6
16
  count?: number;
17
+ /** Second-level destinations. Supplying this turns the entry into a collapsible group: it stops
18
+ * being a destination itself (pressing it expands, never navigates) and `id` becomes the
19
+ * expansion key used by `expandedIds` / `defaultExpandedIds`, not something `onChange` can ever
20
+ * emit. Omit it and the entry behaves exactly as it always has. */
21
+ items?: SideNavChildItem[];
7
22
  }
8
23
  export interface SideNavGroup {
9
24
  label?: string;
@@ -13,10 +28,47 @@ export interface SideNavProps extends Omit<React.HTMLAttributes<HTMLElement>, 'o
13
28
  /** Spaces-and-pages hierarchy: one group per space. */
14
29
  groups: SideNavGroup[];
15
30
  value?: string;
16
- /** Fires with the selected item id — not a DOM change event. */
31
+ /** Fires with the selected item id — not a DOM change event. Only ever a destination id: a
32
+ * first-level entry that owns `items` is a disclosure, and toggling it does not fire this. */
17
33
  onChange?: (id: string) => void;
18
- /** 48px icon rail. Labels move into tooltips. */
34
+ /** Expanded first-level entry ids the controlled half of the pair. Pass it and you own expansion
35
+ * outright, including expanding the group that holds `value`. */
36
+ expandedIds?: string[];
37
+ /** Initial expanded ids for the uncontrolled case. Omit it and the group holding `value` opens
38
+ * itself; supply it and it is taken literally, so `value`'s group can start closed if that is
39
+ * genuinely what you want. */
40
+ defaultExpandedIds?: string[];
41
+ /** Fires with every expanded id after a toggle, controlled or not. */
42
+ onExpandedChange?: (ids: string[]) => void;
43
+ /** 48px icon rail. Labels move into tooltips, and second-level items are not rendered at all —
44
+ * see the note on {@link SideNav}. */
19
45
  collapsed?: boolean;
20
46
  footer?: React.ReactNode;
21
47
  }
22
- export declare function SideNav({ groups, value, onChange, collapsed, footer, className, ...rest }: SideNavProps): React.JSX.Element;
48
+ /** Primary navigation. A first-level entry is either a destination (icon + label, selectable) or a
49
+ * collapsible group (icon + label + chevron) when it carries `items` — the two coexist in one list,
50
+ * which is how a 35-destination ERP menu fits without showing all 35 at once.
51
+ *
52
+ * Both levels are plain `Button`s and the disclosure is React Aria's `Disclosure` /
53
+ * `DisclosureGroup`, so `aria-expanded`, `aria-controls`, the panel's `role="group"` and
54
+ * `aria-labelledby`, and Enter/Space on the trigger all come from React Aria rather than from
55
+ * hand-rolled ARIA here.
56
+ *
57
+ * Three things worth knowing before you use it:
58
+ *
59
+ * - **The panel is always in the DOM.** React Aria hides a collapsed panel with
60
+ * `hidden="until-found"`, applied imperatively in a layout effect, so find-in-page can reveal it.
61
+ * A test asserting a collapsed child is *absent* will fail; assert `aria-expanded` on the trigger,
62
+ * or visibility, instead. The corollary in CSS: any `display` we set on the panel would beat the
63
+ * UA's `[hidden] { display: none }`, leaving collapsed children focusable under
64
+ * `aria-hidden="true"`. `.pire-sidenav-subnav[hidden]` in `components.css` is that guard.
65
+ * - **`collapsed` and expansion are different axes, and the rail wins.** With `collapsed` set there
66
+ * are no labels, and a second level is nothing *but* labels, so children are not rendered in the
67
+ * rail. A group's row becomes an ordinary icon button that records the expansion (so the group is
68
+ * already open when the rail expands) and lights up while it holds `value`, which is the one thing
69
+ * a rail must still communicate. It follows that a nav with collapsible groups needs a way back
70
+ * out of the rail — `ShellBar`'s `onMenu` — or 25 of those 35 destinations are unreachable, for
71
+ * everyone, exactly as the labels already are.
72
+ * - **Several groups can be open at once**, and there is no accordion mode. Closing the group you
73
+ * are in to open another is the wrong trade in a nav whose whole job is showing where you are. */
74
+ export declare function SideNav({ groups, value, onChange, expandedIds, defaultExpandedIds, onExpandedChange, collapsed, footer, className, ...rest }: SideNavProps): React.JSX.Element;
@@ -1,9 +1,24 @@
1
- import type * as React from 'react';
1
+ import * as React from 'react';
2
+ /** A second-level destination, nested under a first-level entry.
3
+ *
4
+ * Deliberately has no `icon`: only the first level carries one, so the second level reads as an
5
+ * indented list of labels under a single glyph rather than a wall of decoration. Leaving `icon` off
6
+ * the *type* is what stops that drifting back in one call site at a time. */
7
+ export interface SideNavChildItem {
8
+ id: string;
9
+ label: string;
10
+ count?: number;
11
+ }
2
12
  export interface SideNavItem {
3
13
  id: string;
4
14
  label: string;
5
15
  icon?: string;
6
16
  count?: number;
17
+ /** Second-level destinations. Supplying this turns the entry into a collapsible group: it stops
18
+ * being a destination itself (pressing it expands, never navigates) and `id` becomes the
19
+ * expansion key used by `expandedIds` / `defaultExpandedIds`, not something `onChange` can ever
20
+ * emit. Omit it and the entry behaves exactly as it always has. */
21
+ items?: SideNavChildItem[];
7
22
  }
8
23
  export interface SideNavGroup {
9
24
  label?: string;
@@ -13,11 +28,48 @@ export interface SideNavProps extends Omit<React.HTMLAttributes<HTMLElement>, 'o
13
28
  /** Spaces-and-pages hierarchy: one group per space. */
14
29
  groups: SideNavGroup[];
15
30
  value?: string;
16
- /** Fires with the selected item id — not a DOM change event. */
31
+ /** Fires with the selected item id — not a DOM change event. Only ever a destination id: a
32
+ * first-level entry that owns `items` is a disclosure, and toggling it does not fire this. */
17
33
  onChange?: (id: string) => void;
18
- /** 48px icon rail. Labels move into tooltips. */
34
+ /** Expanded first-level entry ids the controlled half of the pair. Pass it and you own expansion
35
+ * outright, including expanding the group that holds `value`. */
36
+ expandedIds?: string[];
37
+ /** Initial expanded ids for the uncontrolled case. Omit it and the group holding `value` opens
38
+ * itself; supply it and it is taken literally, so `value`'s group can start closed if that is
39
+ * genuinely what you want. */
40
+ defaultExpandedIds?: string[];
41
+ /** Fires with every expanded id after a toggle, controlled or not. */
42
+ onExpandedChange?: (ids: string[]) => void;
43
+ /** 48px icon rail. Labels move into tooltips, and second-level items are not rendered at all —
44
+ * see the note on {@link SideNav}. */
19
45
  collapsed?: boolean;
20
46
  footer?: React.ReactNode;
21
47
  }
22
- export declare function SideNav({ groups, value, onChange, collapsed, footer, className, ...rest }: SideNavProps): React.JSX.Element;
48
+ /** Primary navigation. A first-level entry is either a destination (icon + label, selectable) or a
49
+ * collapsible group (icon + label + chevron) when it carries `items` — the two coexist in one list,
50
+ * which is how a 35-destination ERP menu fits without showing all 35 at once.
51
+ *
52
+ * Both levels are plain `Button`s and the disclosure is React Aria's `Disclosure` /
53
+ * `DisclosureGroup`, so `aria-expanded`, `aria-controls`, the panel's `role="group"` and
54
+ * `aria-labelledby`, and Enter/Space on the trigger all come from React Aria rather than from
55
+ * hand-rolled ARIA here.
56
+ *
57
+ * Three things worth knowing before you use it:
58
+ *
59
+ * - **The panel is always in the DOM.** React Aria hides a collapsed panel with
60
+ * `hidden="until-found"`, applied imperatively in a layout effect, so find-in-page can reveal it.
61
+ * A test asserting a collapsed child is *absent* will fail; assert `aria-expanded` on the trigger,
62
+ * or visibility, instead. The corollary in CSS: any `display` we set on the panel would beat the
63
+ * UA's `[hidden] { display: none }`, leaving collapsed children focusable under
64
+ * `aria-hidden="true"`. `.pire-sidenav-subnav[hidden]` in `components.css` is that guard.
65
+ * - **`collapsed` and expansion are different axes, and the rail wins.** With `collapsed` set there
66
+ * are no labels, and a second level is nothing *but* labels, so children are not rendered in the
67
+ * rail. A group's row becomes an ordinary icon button that records the expansion (so the group is
68
+ * already open when the rail expands) and lights up while it holds `value`, which is the one thing
69
+ * a rail must still communicate. It follows that a nav with collapsible groups needs a way back
70
+ * out of the rail — `ShellBar`'s `onMenu` — or 25 of those 35 destinations are unreachable, for
71
+ * everyone, exactly as the labels already are.
72
+ * - **Several groups can be open at once**, and there is no accordion mode. Closing the group you
73
+ * are in to open another is the wrong trade in a nav whose whole job is showing where you are. */
74
+ export declare function SideNav({ groups, value, onChange, expandedIds, defaultExpandedIds, onExpandedChange, collapsed, footer, className, ...rest }: SideNavProps): React.JSX.Element;
23
75
  //# sourceMappingURL=SideNav.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/SideNav.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAMpC,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AACzF,MAAM,WAAW,YAAY;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE;AAEtE,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACvF,uDAAuD;IACvD,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,YAAY,qBAwBvG"}
1
+ {"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/SideNav.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B;;;;8EAI8E;AAC9E,MAAM,WAAW,gBAAgB;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAE/E,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;wEAGoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE;AAEtE,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACvF,uDAAuD;IACvD,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;mGAC+F;IAC/F,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC;sEACkE;IAClE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;mCAE+B;IAC/B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C;2CACuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;qGAyBqG;AACrG,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAClG,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,YAAY,qBA+GtD"}
@@ -188,6 +188,28 @@
188
188
  .pire-sidenav-item[data-hovered]{background:var(--surface-hover)}
189
189
  .pire-sidenav-item[data-selected="true"]{background:var(--surface-selected);border-left-color:var(--accent-bg);color:var(--accent-subtle-fg);font:var(--type-body-strong)}
190
190
  .pire-sidenav-count{margin-left:auto;font:var(--type-code);color:var(--text-tertiary)}
191
+ /* Second level. `-tree` is React Aria's DisclosureGroup, `-section` one Disclosure; both exist only
192
+ to keep the rows in a column, since the wrappers are divs the component cannot avoid. */
193
+ .pire-sidenav-tree,.pire-sidenav-section{display:flex;flex-direction:column}
194
+ /* The trailing chevron. Its own auto margin pushes it to the edge on a group row with no count; when
195
+ there is a count, the count's auto margin has already claimed the free space and a second auto here
196
+ would split it and leave the count floating mid-row — hence the reset. */
197
+ .pire-sidenav-chevron{margin-left:auto;color:var(--text-tertiary);transition:transform var(--dur-fast) var(--ease-standard)}
198
+ .pire-sidenav-count + .pire-sidenav-chevron{margin-left:0}
199
+ .pire-sidenav-section:not([data-expanded]) .pire-sidenav-chevron{transform:rotate(-90deg)}
200
+ /* Marks a group row whose child is the current destination: the accent tick and label weight of a
201
+ selection without its fill, so the trail to `aria-current` reads at a glance and still loses to it.
202
+ Also the only cue left in the icon rail, where the children are not rendered. */
203
+ .pire-sidenav-item[data-active-child="true"]{border-left-color:var(--accent-bg);color:var(--accent-subtle-fg);font:var(--type-body-strong)}
204
+ /* React Aria animates the panel through --disclosure-panel-height, and applies `hidden` only once the
205
+ animation settles. The `display` above therefore has to be undone for the collapsed state: it beats
206
+ the UA's `[hidden]{display:none}`, and a visible-to-the-layout panel under `aria-hidden="true"`
207
+ leaves every child in the tab order — an aria-hidden-focus defect, not just a cosmetic one. */
208
+ .pire-sidenav-subnav{display:flex;flex-direction:column;overflow:hidden;height:var(--disclosure-panel-height,auto);transition:height var(--dur-fast) var(--ease-standard)}
209
+ .pire-sidenav-subnav[hidden]{display:none}
210
+ /* Indented to the parent's *label*, not its icon: page padding + the 16px glyph box + the row gap.
211
+ Condensed height because a two-level menu is 35 rows, not 10. */
212
+ .pire-sidenav-subitem{height:var(--row-h-condensed);padding-left:calc(var(--sp-4) + var(--sp-4) + var(--sp-3))}
191
213
  .pire-sidenav-footer{margin-top:auto;padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border-subtle)}
192
214
 
193
215
  /* ---- Feedback -------------------------------------------------------- */
@@ -0,0 +1,93 @@
1
+ Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) JetBrainsMono-Italic[wght].ttf: Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.