import{type TemplateResult,type PropertyValues}from'lit';import{LyraElement}from'../../../internal/lyra-element.js';import'../../layout/segmented/segmented.class.js';import'../slider/slider.class.js';import'../select/select.class.js';import'../combobox/option.class.js';import'../checkbox/checkbox.class.js';import'../checkbox-group/checkbox-group.class.js';import'../textarea/textarea.class.js';import{type FormOwnerValue}from'../../../internal/form-associated.js';export interface RubricKeyOption{readonly value:string;readonly label?:string;readonly description?:string;}interface RubricKeyBase{readonly key:string;readonly label?:string;readonly description?:string;readonly required?:boolean;}export interface ScoreRubricKey extends RubricKeyBase{readonly type:'score';readonly min?:number;readonly max?:number;readonly step?:number;}export interface CategoryRubricKey extends RubricKeyBase{readonly type:'category';readonly options?:readonly RubricKeyOption[];readonly multiple?:boolean;}export interface CommentRubricKey extends RubricKeyBase{readonly type:'comment';readonly placeholder?:string;} /** Discriminated, immutable field schema; each variant exposes only meaningful configuration. */ export type RubricKey=ScoreRubricKey|CategoryRubricKey|CommentRubricKey;export type RubricValue=Readonly>;export interface LyraRubricFormEventMap{'lr-invalid':CustomEvent;'lr-input':CustomEvent>;'lr-validity-change':CustomEvent<{readonly valid:boolean;readonly errors:Readonly>;}>;'lr-submit':CustomEvent>;'lr-skip':CustomEvent<{itemId:string;}>;} /** * `` — a configurable annotation rubric (LangSmith * annotation-queue style): score, category, and freeform-comment keys with * a submit-and-next flow for working through an eval queue. * * Each `RubricKey.type` routes to an existing sibling control: `score` * renders `` when its `[min, max]`/`step` domain has 10 or * fewer integer steps, or `` otherwise; `category` renders * `` (single) or `` (`multiple`); `comment` * renders ``. Both score branches format visible numeric labels with the effective * locale; segmented item values and submitted rubric values remain stable raw numbers/strings. * A key whose `type` is none of the three renders * a visible "Unsupported field type" note instead of silently dropping it, * and marks the form invalid — the same defensive shape as * ``'s own unsupported-property fallback. * The exported key model is a readonly discriminated union. Live/default/restored values are * canonicalized once against that current schema before render, validity, events, or FormData: * scores clamp/snap to their domain, categories retain only declared option occurrences, * comments require strings, and undeclared fields are discarded. A wide slider score without a * supplied default records its rendered, snapped midpoint in both the default and live value; * segmented scores remain unselected. `defaultValue` is the explicit reset baseline; reads return * defensive snapshots. * Replacing the parent value reconciles live multiple-category checkbox state, even immediately * after a user edit, without changing child reset defaults or emitting user-edit events. * * Optional native `
` participation is implemented via `ElementInternals` * attached directly (this component's value is a whole object, not a plain * string, so the `FormAssociated` string-value mixin doesn't fit) — the same * shape `` uses. This is a nice-to-have layered on top * of the primary integration contract (`value` + * `lr-input`/`lr-validity-change`/`lr-submit`/`lr-skip`), not a * requirement: a consumer that never puts this inside a `` loses * nothing. * Aggregate `label`, `hint`, and `errorText` properties have matching slots and * same-shadow ARIA links on the outer `base` role="group". A host `aria-label` wins by attribute * presence (including an explicitly empty value), while each rubric field keeps its own * field-level name. When `errorText` is empty, a consumer `setCustomValidity()` message is rendered * in the aggregate error region so a blocking whole-form error is never silent. * Host-root external descriptions precede the aggregate hint/error guidance and follow live source * identity changes, reconnection and adoption. They stay on the aggregate group; child fields keep * their own guidance. Removing label or hint safely removes that copy while retaining native * attribute-removal property readback. * * @customElement lr-rubric-form * @slot label - Aggregate rubric label rendered before all fields. * @slot hint - Aggregate supporting text rendered after all fields. * @slot error - Aggregate validation text; supplements `errorText` or the current custom error. * @slot actions - Extra host controls rendered in the footer beside Submit/Skip. * @event lr-input - `detail: { value }` — any control changed; the full current value object. * @event lr-validity-change - `detail: { valid, errors }` — fired only on an actual change. * @event lr-submit - `detail: { value, itemId }` — Submit clicked or Ctrl/Cmd+Enter, after validity passes. * @event lr-skip - `detail: { itemId }` — Skip activated (`skippable` only); no validation. * @event lr-invalid - The complete rubric form failed a validity check. Cancelable; preventing it * also prevents the native `invalid` event's default validation UI. * @csspart base - The outer wrapper around all fields. * @csspart form-control - Aggregate form-control wrapper around label, fields, hint, and error. * @csspart aggregate-label - Aggregate rubric label; separate from per-field `label` parts. * @csspart form-control-label - Shared form-control alias on the aggregate label. * @csspart fields - Wrapper around every rubric field or the empty state. * @csspart form-control-input - Shared form-control alias on the fields wrapper. * @csspart aggregate-hint - Aggregate supporting text; also carries `form-control-help-text`. * @csspart form-control-help-text - Shared form-control alias on the aggregate hint. * @csspart aggregate-error - Aggregate error text; also carries `form-control-error`. * @csspart form-control-error - Shared form-control alias on the aggregate error. * @csspart field - One key's wrapper (label + control + description + error). * @csspart label - A field's label. * @csspart description - A field's helper text. * @csspart scale - The rendered score/category/comment control's wrapper. * @csspart error - Ordinary field-level validation text composed into its control's accessible * name/description; it is not a live region, avoiding duplicate report-validity announcements. * @csspart footer - The row containing the actions slot and Submit/Skip buttons. * @csspart submit - The Submit button. * @csspart skip - The Skip button (only rendered when `skippable`). * @csspart empty - The message shown when `keys` has no entries. * @csspart unsupported - The fallback note for a key whose `type` is outside the three supported ones. * @cssprop [--lr-rubric-form-submit-bg=var(--lr-color-brand)] - Submit-button background. * @cssprop [--lr-rubric-form-submit-border-color=var(--lr-color-brand)] - Submit-button border. * @cssprop [--lr-rubric-form-submit-color=var(--lr-color-on-brand)] - Submit-button text color. * @cssprop [--lr-rubric-form-submit-hover-bg=color-mix(...)] - Submit background while hovered. * @cssprop [--lr-rubric-form-submit-hover-border-color=color-mix(...)] - Submit border while * hovered. * @cssprop [--lr-rubric-form-submit-active-bg=color-mix(...)] - Submit background while pressed. * @cssprop [--lr-rubric-form-submit-active-border-color=color-mix(...)] - Submit border while * pressed. * @cssprop [--lr-rubric-form-skip-bg=var(--lr-color-surface)] - Skip-button background. * @cssprop [--lr-rubric-form-skip-border-color=var(--lr-color-border)] - Skip-button border. * @cssprop [--lr-rubric-form-skip-color=var(--lr-color-text)] - Skip-button text color. * @cssprop [--lr-rubric-form-skip-hover-bg=var(--lr-color-brand-quiet)] - Skip background while * hovered. * @cssprop [--lr-rubric-form-skip-active-bg=color-mix(...)] - Skip background while pressed. * @cssprop [--lr-form-control-required-content=' *'] - Required-field marker after rubric-owned * score and unsupported-field labels. Set it to `''` to suppress the marker, or to any other * quoted string (`' (required)'`, a localized word) to replace it. * @cssprop [--lr-form-control-required-color=var(--lr-color-danger)] - Color of that marker, * independently retunable from field errors. * @cssprop [--lr-form-control-required-offset=0] - Inline space between the label text and marker. * @cssstate required - Matches while at least one `RubricKey` is `required` — this control has no * `required` property of its own, so "required" means the rubric cannot be submitted empty. * @cssstate optional - Matches while no key is required (the complement of `required`). * @cssstate valid - Matches while every required key is answered and no key has an unsupported * `type`. * @cssstate invalid - Matches while it does not — including a pristine rubric with unanswered * required keys, exactly like native `:invalid`. * @cssstate user-valid - `valid`, but only after the user has interacted: visited a field, been * through a `reportValidity()`/Submit attempt (which reveals every outstanding error), or a * form submission attempt. Not after a silent `checkValidity()` alone. * @cssstate user-invalid - `invalid`, but only after that same interaction — a rubric nobody has * touched yet is invalid without being styled as an error. * @status stable * @since 4.0.0 */ export declare class LyraRubricForm extends LyraElement{static formAssociated:boolean;static styles:import("lit").CSSResultGroup[];static properties:{customError:{attribute:string;reflect:boolean;noAccessor:boolean;};name:{reflect:boolean;noAccessor:boolean;};keys:{attribute:boolean;noAccessor:boolean;};value:{attribute:boolean;noAccessor:boolean;};defaultValue:{attribute:boolean;noAccessor:boolean;};itemId:{attribute:string;reflect:boolean;noAccessor:boolean;};hasNext:{type:BooleanConstructor;attribute:string;noAccessor:boolean;};skippable:{type:BooleanConstructor;noAccessor:boolean;};disabled:{type:BooleanConstructor;reflect:boolean;noAccessor:boolean;};}; /** Aggregate label for the complete rubric. Rich content can use the matching slot. */ label:string; /** Aggregate supporting text. */ hint:string; /** Aggregate visible error text. A consumer custom-validity message is shown when this is empty. */ errorText:string; /** SSR label-presence hint; hydrated instances also discover populated slots. */ withLabel:boolean; /** SSR hint-presence hint; hydrated instances also discover populated hint/help-text slots. */ withHint:boolean;private _errors;private touchedFields;private hasLabelSlot;private hasHintSlot;private hasErrorSlot;private aggregateLabelSlotEl?;private aggregateHintSlotEl?;private aggregateErrorSlotEl?;private internals;private validityController; /** Consumer-supplied validation message reflected through `custom-error`. */ customError:string|null;private baseId;private aggregateLabelId;private aggregateHintId;private aggregateErrorId;private _fieldsetDisabled;private _name;private _keys;private _value;private _defaultValue;private _rawValue;private _rawDefaultValue;private _valueDirty;private _itemId;private _hasNext;private _skippable;private _disabled;private lastValidityKey;private pendingFocusFirst;constructor(); /** `attachInternals()` throws in any environment without a real `ElementInternals` * implementation (e.g. a downstream consumer's happy-dom test suite) -- merely constructing * (or importing) this component must not hard-crash there. Falls back to an inert stand-in: * form participation and validity reporting are unavailable in that environment (there is no * polyfillable substitute), but rendering and every non-form-associated feature keep working. * Mirrors lr-graph-query-builder's identical guard. */ private safeAttachInternals;private inertInternals;get form():HTMLFormElement|null;set form(owner:FormOwnerValue);getForm():HTMLFormElement|null; /** Delegates straight to `ElementInternals.labels` -- no logic of its own. */ get labels():NodeList;get validity():ValidityState;get validationMessage():string;get willValidate():boolean;get keys():readonly RubricKey[];set keys(next:readonly RubricKey[]); /** Canonical live structured value. Reads are defensive snapshots; writes are normalized against * the current discriminated schema before rendering, validity, events, and form submission. */ get value():RubricValue;set value(next:RubricValue); /** Explicit native form-reset baseline. Changing it updates pristine live state only. */ get defaultValue():RubricValue;set defaultValue(next:RubricValue);private setLiveValue;get itemId():string;set itemId(next:string);get hasNext():boolean;set hasNext(next:boolean);get skippable():boolean;set skippable(next:boolean);get name():string;set name(next:string);get disabled():boolean;set disabled(next:boolean); /** Whether the form is disabled explicitly or by an ancestor fieldset. */ get effectiveDisabled():boolean; /** The current effective validation errors. Intrinsic errors are keyed by their rubric key; * a caller-supplied custom validity message is keyed by the whole-control `base` part. */ get errors():Readonly>;private publicValidityErrors;private publishValiditySnapshot;private fieldElement;private isSegmentedScore;private scoreValues;private computeValidation;private syncFormState; /** * Shared with every other form control in the library: own `disabled` and a `
` * ancestor both bar constraint validation, so a barred rubric reports no failure and publishes * neither `:state(invalid)` nor `:state(user-invalid)` — see `internal/custom-states.ts`. */ private get barredFromValidation(); /** * Publishes the six validity custom states (`:state(required)`/`optional`, `valid`/`invalid`, * `user-valid`/`user-invalid`) through the shared helper in `internal/custom-states.ts` — this * component attaches `ElementInternals` directly (its value is a whole object, which the * string-value `FormAssociated` mixin cannot carry), so it calls that helper itself rather than * inheriting the call. * * Two mappings are specific to a multi-field rubric. `required` is not a property here but a * per-key flag, so the host is `:state(required)` exactly when at least one of its keys is — * i.e. when the rubric cannot be submitted empty. And "the user has interacted" is * `touchedFields` being non-empty: any field visited, or a `reportValidity()`/submit attempt, * which reveals every outstanding error at once. */ private syncCustomStates;private markInteracted; /** Resynchronizes validity without revealing inline errors. */ checkValidity():boolean; /** * Reveals every current field error (as if each field had been visited) * and returns overall validity -- the hook Submit calls before acting, * mirroring a native ``'s `reportValidity()`. */ reportValidity():boolean; /** * Sets or clears a consumer-supplied validation error — the standard channel for a server-side * rejection ("this item was already annotated by someone else") that no per-key rule can * express. A non-empty `message` raises `customError` and becomes `validationMessage`, so the * rubric fails `checkValidity()`, blocks form submission, and matches `:state(invalid)`; `''` * clears it. * * Clearing restores the rubric's own computed validity rather than forcing it valid: unanswered * required keys (and any key with an unsupported `type`) still hold it invalid. The custom error * also survives every intrinsic recomputation in between (each `value`/`keys` write re-runs * `syncFormState()`) and a form reset, exactly like a native control — only another * `setCustomValidity('')` clears it. * * The message is form-level and is exposed as `errors.base`, keyed to this control's `base` * part. It is caller-supplied content, so it is used verbatim and never localized here. */ setCustomValidity(message:string):void;formResetCallback():void;formStateRestoreCallback(state:string|File|FormData|null,_mode?:'restore'|'autocomplete'):void;formDisabledCallback(disabled:boolean):void;private onFormKeyDown;private setFieldValue;private stopChildEvent;private onAggregateSlotChange; /** * Reads one aggregate label/hint/error slot's currently assigned content and applies it -- * wired as the `slotchange` handler (via `onAggregateSlotChange`) for every later mutation, and * called once more from `firstUpdated()` (see `collectInitialSlotAssignment`) to cover an * environment, or a real-browser timing race, where the slot's initial assignment never fires * `slotchange`. Idempotent: re-deriving the same boolean from the same assigned-node set * produces the same result on a second call. */ private applyAggregateSlotAssignment;private markTouched;private submit;private skip;private focusFirstControl; /** Forwards host clicks to the first rendered field, so programmatic and `