courthive-components 4.5.0 → 6.0.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.
package/README.md CHANGED
@@ -320,6 +320,39 @@ Import the bundled CSS:
320
320
  import 'courthive-components/dist/courthive-components.css';
321
321
  ```
322
322
 
323
+ ### Theme tokens — two families, on purpose
324
+
325
+ Colour is driven entirely by CSS custom properties defined in `src/styles/theme.css`, in a `:root`
326
+ block (light) and a `[data-theme='dark']` block. **Never hardcode a colour in component CSS**, and
327
+ never use Bulma classes or `--bulma-*`: class names that merely follow Bulma's naming are ours and
328
+ are fine, but the palette must resolve through a token or it cannot follow the theme.
329
+
330
+ | family | what it is | use it for |
331
+ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
332
+ | `--chc-*` | The general design system. Semantic roles — `--chc-text-primary`, `--chc-bg-elevated`, `--chc-border-primary`, `--chc-container-*` intents and their `--chc-on-*` foregrounds. | Anything shared: draws, cards, tables, buttons, notifications. |
333
+ | `--sp-*` | A slate palette with translucent, layered surfaces (`--sp-panel-bg`, `--sp-card-bg`, `--sp-border`) plus finer border and status intensities. | The schedule page, scheduling profile, policy catalog, topology builder and interactive scoring — surfaces designed as panels over a ground. |
334
+
335
+ They are **not** duplicates. `--chc-*` began as a compatibility layer whose light values match
336
+ previously hardcoded ones; `--sp-*` is a newer, denser palette with alpha-composited surfaces
337
+ `--chc-*` has no equivalent for. Consumers bridge both: TMX aliases 29 `--chc-*` and 13 `--sp-*` to
338
+ its own `--tmx-*` system.
339
+
340
+ **Rules that keep them working:**
341
+
342
+ - **Every token must be defined** in `theme.css`, in both blocks. A token used but never defined
343
+ renders its call-site fallback in _both_ themes — which is how `--sp-border-focus` shipped an
344
+ invisible focus border.
345
+ - **Do not write a fallback.** `var(--sp-muted)`, never `var(--sp-muted, #888)`. Fallbacks drift from
346
+ the definition — `--sp-muted` once had five different ones — and the token stops being a single
347
+ source of truth.
348
+ - **The library must not reference a consumer's tokens.** `--tmx-*` belongs to TMX; reaching for it
349
+ from here inverts the dependency.
350
+ - **`--chc-on-*` is the readable foreground for a `--chc-container-*` fill.** Both themes are held to
351
+ WCAG AA by `src/styles/theme-contrast.test.ts`; change a container colour and that test tells you
352
+ whether its foreground still works.
353
+ - Brand themes (`src/styles/themes.css`, `.chc-theme-*`) keep literal colours. A brand does not
354
+ follow the light/dark theme.
355
+
323
356
  ## TypeScript Support
324
357
 
325
358
  The package includes TypeScript definitions:
@@ -23,6 +23,17 @@ export declare class ScoringEditorStore {
23
23
  isAllowedFormatDuplicate(format: string, excludeIndex?: number): boolean;
24
24
  addAllowedFormatCategory(index: number, kind: 'categoryNames' | 'categoryTypes', value: string): void;
25
25
  removeAllowedFormatCategory(index: number, kind: 'categoryNames' | 'categoryTypes', valueIndex: number): void;
26
+ /**
27
+ * Add a code to a status group.
28
+ *
29
+ * The operator types a code; the entry stored is an OBJECT. Only `matchUpStatusCode` is set —
30
+ * `matchUpStatusCodeDisplay` and `label` are authored per code and this control has no field for
31
+ * them, so inventing values would be worse than leaving them absent (the reason picker falls back
32
+ * to the code, which is the policy saying "the code IS the label").
33
+ *
34
+ * Entries that arrive already carrying display text are never rewritten by this path, which is
35
+ * the whole point: the previous `string[]` model discarded them on every save.
36
+ */
26
37
  addStatusCode(status: MatchUpStatusKey, value: string): void;
27
38
  removeStatusCode(status: MatchUpStatusKey, index: number): void;
28
39
  addProcessCode(value: string): void;
@@ -1,14 +1,4 @@
1
- /**
2
- * Scoring Policy Editor — Type Definitions
3
- *
4
- * Mirrors the factory's scoring policy data shape (see
5
- * factory/src/fixtures/policies/POLICY_SCORING_DEFAULT.ts).
6
- *
7
- * The factory does not export a TypeScript interface for the scoring
8
- * policy itself — the fixture defines the shape via runtime defaults.
9
- * We re-derive the shape here so the editor can stay type-safe without
10
- * dragging factory internals into courthive-components.
11
- */
1
+ import { StatusCodeEntry } from '../../../scoring/logic/statusCodes';
12
2
  export type MatchUpStatusKey = 'ABANDONED' | 'CANCELLED' | 'DEFAULTED' | 'INCOMPLETE' | 'RETIRED' | 'WALKOVER';
13
3
  export declare const MATCH_UP_STATUS_KEYS: MatchUpStatusKey[];
14
4
  export type RequireAllPositionsTriState = 'default' | 'true' | 'false';
@@ -36,7 +26,7 @@ export interface ScoringPolicyData {
36
26
  requireAllPositionsAssigned?: boolean;
37
27
  allowChangePropagation?: boolean;
38
28
  allowDeletionWithScoresPresent?: AllowDeletionWithScoresPresent;
39
- matchUpStatusCodes?: Partial<Record<MatchUpStatusKey, string[]>>;
29
+ matchUpStatusCodes?: Partial<Record<MatchUpStatusKey, StatusCodeEntry[]>>;
40
30
  stage?: Record<string, {
41
31
  stageSequence?: Record<string, StageSequenceOverride>;
42
32
  }>;
@@ -0,0 +1,5 @@
1
+ import { NormalisedSeedingProfile, SeedingPolicyData, SeedingPolicyDraft } from '../types';
2
+ /** Widen a policy's `seedingProfile` — and every drawType override — to the object form. */
3
+ export declare function normaliseSeedingProfile(profile: SeedingPolicyData['seedingProfile']): NormalisedSeedingProfile | undefined;
4
+ /** Widen a whole policy to the editor's internal draft form. */
5
+ export declare function toDraft(policy: SeedingPolicyData): SeedingPolicyDraft;
@@ -8,3 +8,12 @@ export declare const POSITIONING_OPTIONS: {
8
8
  export declare const DRAW_TYPE_OPTIONS: string[];
9
9
  export declare function emptySeedingPolicy(): SeedingPolicyData;
10
10
  export declare function positioningLabel(value: SeedingPositioning | undefined): string;
11
+ /**
12
+ * `ADJACENT` is the factory's synonym for `CLUSTER` — `generateBlockPattern` and
13
+ * `getContainerBlocks` treat them identically. The picker deliberately offers ONE option
14
+ * rather than two that do the same thing, so either stored value selects it.
15
+ *
16
+ * This is display-only: the stored value is left exactly as it arrived, because rewriting
17
+ * a caller's `ADJACENT` to `CLUSTER` would be an unrequested edit to their policy.
18
+ */
19
+ export declare function canonicalPositioning(value?: SeedingPositioning): SeedingPositioning | undefined;
@@ -5,4 +5,4 @@ export { SeedingEditorControl, createSeedingEditor } from './seedingEditorContro
5
5
  export { SeedingEditorStore } from './seedingEditorStore';
6
6
  export { buildSeedingEditorPanel } from './seedingEditorPanel';
7
7
  export { emptySeedingPolicy, POSITIONING_OPTIONS, DRAW_TYPE_OPTIONS } from './domain/seedingProjections';
8
- export type { SeedingPolicyData, SeedingProfile, SeedingPositioning, SeedsCountThreshold, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig } from './types';
8
+ export type { SeedingPolicyData, SeedingPolicyDraft, NormalisedSeedingProfile, PolicySeedingProfile, DrawSeedingProfile, SeedingProfile, SeedingPositioning, SeedsCountThreshold, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig } from './types';
@@ -1,5 +1,5 @@
1
1
  import { SeedingEditorStore } from './seedingEditorStore';
2
- import { SeedingPolicyData, SeedingEditorConfig } from './types';
2
+ import { SeedingPolicyDraft, SeedingPolicyData, SeedingEditorConfig } from './types';
3
3
  import { PolicyEditorInstance as CatalogEditorInstance } from '../../types';
4
4
  export declare class SeedingEditorControl {
5
5
  private readonly store;
@@ -9,7 +9,7 @@ export declare class SeedingEditorControl {
9
9
  constructor(config: SeedingEditorConfig);
10
10
  render(container: HTMLElement): void;
11
11
  destroy(): void;
12
- getData(): SeedingPolicyData;
12
+ getData(): SeedingPolicyDraft;
13
13
  setData(data: SeedingPolicyData): void;
14
14
  getStore(): SeedingEditorStore;
15
15
  static createEditorInstance(config: {
@@ -1,11 +1,11 @@
1
- import { SeedingPolicyData, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig, SeedsCountThreshold, SeedingPositioning } from './types';
1
+ import { SeedingPolicyDraft, SeedingPolicyData, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig, SeedsCountThreshold, SeedingPositioning } from './types';
2
2
  export declare class SeedingEditorStore {
3
3
  private state;
4
4
  private readonly listeners;
5
5
  private readonly config;
6
6
  constructor(config: SeedingEditorConfig);
7
7
  getState(): SeedingEditorState;
8
- getData(): SeedingPolicyData;
8
+ getData(): SeedingPolicyDraft;
9
9
  setData(data: SeedingPolicyData): void;
10
10
  toggleSection(sectionId: SeedingEditorSection): void;
11
11
  setPolicyName(value: string): void;
@@ -1,41 +1,42 @@
1
+ import { SeedingProfile as DrawSeedingProfile, PolicySeedingProfile, SeedingProfileUnion, SeedsCountThreshold, SeedingPolicy } from 'tods-competition-factory';
2
+ export type { SeedsCountThreshold, PolicySeedingProfile, DrawSeedingProfile };
3
+ /** `'ADJACENT' | 'CLUSTER' | 'SEPARATE' | 'WATERFALL'` — the factory's closed union. */
4
+ export type SeedingPositioning = SeedingProfileUnion;
5
+ /** The policy-level profile in its object form. */
6
+ export type SeedingProfile = PolicySeedingProfile;
7
+ /** What the editor accepts and emits — the factory's policy shape, verbatim. */
8
+ export type SeedingPolicyData = SeedingPolicy;
1
9
  /**
2
- * Seeding Policy Editor — Type Definitions
10
+ * The editor's INTERNAL draft.
3
11
  *
4
- * Mirrors the factory's seeding policy data shape (see
5
- * factory/src/fixtures/policies/POLICY_SEEDING_*.ts).
12
+ * `SeedingPolicy.seedingProfile` is `PolicySeedingProfile | SeedingProfileUnion` — the
13
+ * factory still honours a bare positioning string where a profile object is expected.
14
+ * The editor holds and mutates an object, so the string form is normalised away on
15
+ * ingest (see `normaliseSeedingProfile`) and the draft is the object form only.
6
16
  */
7
- export type SeedingPositioning = 'SEPARATE' | 'WATERFALL' | 'CLUSTER';
8
- export interface SeedsCountThreshold {
9
- drawSize: number;
10
- minimumParticipantCount: number;
11
- seedsCount: number;
12
- }
13
- export interface SeedingProfile {
14
- positioning?: SeedingPositioning;
15
- drawTypes?: Record<string, {
16
- positioning: SeedingPositioning;
17
- }>;
18
- }
19
- export interface SeedingPolicyData {
20
- policyName?: string;
21
- seedingProfile?: SeedingProfile;
22
- validSeedPositions?: {
23
- ignore: boolean;
24
- };
25
- duplicateSeedNumbers?: boolean;
26
- drawSizeProgression?: boolean;
27
- seedsCountThresholds?: SeedsCountThreshold[];
28
- }
17
+ export type SeedingPolicyDraft = Omit<SeedingPolicyData, 'seedingProfile'> & {
18
+ seedingProfile?: NormalisedSeedingProfile;
19
+ };
20
+ /**
21
+ * A {@link PolicySeedingProfile} whose drawType overrides are known to be objects.
22
+ * The factory types each override as `SeedingProfile | SeedingProfileUnion`; the editor
23
+ * normalises the string form away on ingest, so the draft need not re-narrow at every read.
24
+ */
25
+ export type NormalisedSeedingProfile = Omit<PolicySeedingProfile, 'drawTypes'> & {
26
+ drawTypes?: Record<string, DrawSeedingProfile>;
27
+ };
29
28
  export type SeedingEditorSection = 'profile' | 'flags' | 'thresholds' | 'drawTypeOverrides';
30
29
  export interface SeedingEditorState {
31
- draft: SeedingPolicyData;
30
+ draft: SeedingPolicyDraft;
32
31
  expandedSections: Set<SeedingEditorSection>;
33
32
  dirty: boolean;
34
33
  }
35
34
  export type SeedingEditorChangeListener = (state: SeedingEditorState) => void;
36
35
  export interface SeedingEditorConfig {
36
+ /** Accepts either form the factory accepts; the string form is normalised on ingest. */
37
37
  initialPolicy?: SeedingPolicyData;
38
38
  /** Override the list of drawTypes offered in the overrides picker. */
39
39
  drawTypes?: string[];
40
- onChange?: (policy: SeedingPolicyData) => void;
40
+ /** Emits the normalised object form, which is itself a valid {@link SeedingPolicyData}. */
41
+ onChange?: (policy: SeedingPolicyDraft) => void;
41
42
  }
@@ -24,5 +24,5 @@ export type { PrivacyPolicyData, PrivacyEditorConfig, PrivacyFieldMeta } from '.
24
24
  export type { PolicyCatalogItem, PolicyCatalogState, PolicyCatalogChangeListener, PolicyCatalogConfig, PolicyEditorInstance, PolicyEditorPlugin, PolicySource, PolicyTypeGroup, PolicyTypeMeta, CatalogGroupBy, UIPanel } from './types';
25
25
  export type { SchedulingPolicyData, SchedulingEditorState, SchedulingEditorSection, SchedulingEditorChangeListener, SchedulingEditorConfig, SchedulingValidationResult, ValidationSeverity, MinutesEntry, AverageTimeEntry, RecoveryTimeEntry, MatchUpAverageTime, MatchUpRecoveryTime } from './editors/scheduling';
26
26
  export type { RankingPolicyData, RankingPointsEditorState, RankingEditorSection, RankingPointsEditorChangeListener, RankingPointsEditorConfig, AwardProfileData, QualityWinProfileData, AggregationRulesData, TableLayout } from './editors/ranking';
27
- export type { SeedingPolicyData, SeedingProfile, SeedingPositioning, SeedsCountThreshold, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig } from './editors/seeding';
27
+ export type { SeedingPolicyData, SeedingPolicyDraft, NormalisedSeedingProfile, PolicySeedingProfile, DrawSeedingProfile, SeedingProfile, SeedingPositioning, SeedsCountThreshold, SeedingEditorState, SeedingEditorSection, SeedingEditorChangeListener, SeedingEditorConfig } from './editors/seeding';
28
28
  export declare function createPolicyCatalog(config: PolicyCatalogConfig, container: HTMLElement): PolicyCatalogControl;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Why a seed exists, for the on-screen draw.
3
+ *
4
+ * `Side.seedingBasis` records the ground on which a seeding was awarded, as distinct from
5
+ * `seedValue`, which says only where it sits in the order. It matters wherever a governing body
6
+ * permits seeds ABOVE the count its policy allows — a protected ranking seeded ALONGSIDE the normal
7
+ * seeds rather than in place of one. Such a seed displaced nobody, and it is the one somebody will
8
+ * ask about.
9
+ *
10
+ * ## Why the bracket is marked here and not on the printed sheet
11
+ *
12
+ * pdf-factory deliberately leaves the bracket alone and footnotes its **seedings table** instead:
13
+ * an entry line there already carries name, nationality and an entry-status badge, and the table
14
+ * exists to answer "why these seeds". The on-screen draw has **no seedings table** — the bracket is
15
+ * the only surface the fact can appear on, so leaving it unmarked leaves it nowhere.
16
+ *
17
+ * The marker is a single character appended inside the seed's own brackets, so it costs no layout;
18
+ * the basis itself goes in a `title`, following the same rule the presence work settled on — the
19
+ * fact is displayed, the detail is on hover.
20
+ *
21
+ * `RANKING` is the ordinary basis and is never marked. An absent basis means the ordinary one, not
22
+ * "unknown", so marking every seed would bury the one that is not.
23
+ */
24
+ /** The marker to append to a seed display, or `''` where the seed is ordinary. */
25
+ export declare function seedingBasisMarker(seedingBasis?: string): string;
26
+ /**
27
+ * The hover text for a marked seed, or `undefined` where there is nothing to say.
28
+ *
29
+ * An unrecognised basis is printed verbatim rather than dropped. The factory's basis enum can gain
30
+ * members, and a tooltip that silently omits one it does not know is worse than a tooltip showing
31
+ * an unfamiliar word — the second is a question, the first is a wrong answer.
32
+ */
33
+ export declare function seedingBasisTitle(seedingBasis?: string): string | undefined;