Yew interface

The tables on this page are checked against the Rust source. A test reads the struct that backs each table and fails when a field, a type or a default differs from what is written here. The code blocks that show usage are regions of prose-yew/tests/site_examples.rs, which cargo test -p prose-yew compiles and renders on the server.

Four components exist: OdrlProse, OdrlProseView, ProseGlossary and ProseProblems. The first renders a policy given as JSON text. The second renders and edits a parsed document, and is controlled: it never changes the document, and the host applies every event it emits. Three hooks keep the host's state: use_prose_editor, use_prose_editor_with and use_validation.

OdrlProse #

A function component that renders ODRL JSON-LD as semantic HTML with prose_core::render_with. It emits no <style>. The host styles it through the prose-* classes, or through class on the root.

PropertyTypeDefaultMeaning
jsonAttrValuerequiredThe ODRL policy as JSON-LD text.
classClassesemptyAdded to the root article, after ds-prose.
wordingOption<SharedWording>NoneThe loaded profiles. None reads as before 0.4.
glossarybooltrueShow a glossary of profile terms after each policy. It appears only when a profile explains a term the policy uses.
validatorOption<Validator>NoneValidate against the validator's profiles. Nothing is shown for it unless violations is set.
violationsboolfalseAdd a section after the policies with the findings and the verdict. Needs validator.
linksRc<BTreeSet<String>>emptyIRIs the host found to dereference. A condition or a rule sentence that refers to a constraint declared at one of them, outside the document, shows the IRI as an a.prose-reference, and a party, an asset or a value named by one of them is an a.prose-iri; any other IRI is not a link. The component fetches nothing.

SharedWording wraps an Rc<Wording>. Two values are equal when they are the same Rc, so the component renders again only when the host hands it another Rc.

A policy that cannot be read renders <article class="ds-prose"><p class="prose-error" role="alert">. A struct literal of OdrlProseProps needs all six fields, because 0.4 added four of them; html! fills the defaults.

#[function_component(Reader)]
fn reader() -> Html {
    html! { <OdrlProse json={POLICY} /> }
}

From prose-yew/tests/site_examples.rs, region reader.

With a profile, the glossary switched off and a class:

#[function_component(ReaderWithWording)]
fn reader_with_wording() -> Html {
    let wording = SharedWording(Rc::new(Wording::new()));
    html! { <OdrlProse json={POLICY} wording={Some(wording)} glossary={false} class="my-prose" /> }
}

From prose-yew/tests/site_examples.rs, region reader_wording.

OdrlProseView #

Renders a parsed EditDoc in read or edit mode. In edit mode the slots are contenteditable, the selects and the add and remove buttons appear, and focus is managed. Every committed edit leaves through onedit as an EditEvent. A structural event whose new document has not arrived within 500 ms is treated as refused: focus does not move and nothing is announced.

PropertyTypeDefaultMeaning
docRc<EditDoc>requiredThe document. The host owns it.
modeEditModeEditMode::ReadRead or edit.
configRc<EditConfig>Rc::new(EditConfig::default())Everything in the EditConfig section below.
oneditCallback<EditEvent>no-opEvery committed edit and structural action. Not applied by the component.
on_focus_slotCallback<Option<SlotFocus>>no-opSome when a slot or select gets focus, None when it loses it. For help text.
issuesRc<Vec<Issue>>emptyFindings the host computed, shown at their targets. An error sets aria-invalid and shows its message.
refusalOption<Issue>NoneAn edit the host refused, shown with the issues at its slot. Normally error.issue(&event) of ProseEditorWith::refused.
decorateOption<Callback<Decoration, Html>>NoneHost badges after each policy heading, rule sentence and condition.
classClassesemptyExtra classes on the root, appended last.

The root is <article class="ds-prose prose-view {config.classes.root} [prose-edit [prose-reveal-hover]] {class}" tabindex="-1">. prose-edit is present only in edit mode, and prose-reveal-hover only in edit mode with Reveal::OnHoverOrFocus. In edit mode with base_css on, the first child of the root is the <style> element holding BASE_CSS. Read mode never writes a <style>.

#[function_component(ReadOnlyView)]
fn read_only_view() -> Html {
    let doc = use_memo((), |_| read_model(POLICY).unwrap_or_default());
    html! { <OdrlProseView doc={doc} /> }
}

From prose-yew/tests/site_examples.rs, region read_view.

The edit layer reads bare property keys only (permission, not odrl:permission, and no full IRIs). OdrlProse reads all three. A document written with prefixed keys renders in OdrlProse and reads as no rules in read_model.

EditMode #

ProseGlossary #

Renders the profile terms one policy uses as <details class="prose-glossary" open> with a <dl>. OdrlProse uses it for you. Use it directly to place a glossary elsewhere. It renders nothing for an empty glossary, and shows at most 30 entries.

PropertyTypeDefaultMeaning
glossaryPolicyGlossaryrequiredThe terms to show.
labelsRc<Labels>Labels::default()The words of the glossary.

ProseProblems #

The findings of a validation, grouped by rule, each with a "Go to" button and one button for each fix. See Validation.

PropertyTypeDefaultMeaning
reportRc<Report>requiredThe report, for example from use_validation.
onjumpCallback<IssueTarget>no-opA "Go to" button was pressed. The host moves focus to the target, a [data-slot] or [data-node] element of the view.
onfixCallback<Vec<EditEvent>>no-opA fix button was pressed. Apply the events as one, with ProseEditorWith::apply_all. They carry expect ids, so a fix from a report the document has moved on from is refused as stale.
labelsRc<Labels>Labels::default()The words of the panel.
classClassesemptyAdded to the panel root.

Hooks #

A ready-made host. It owns the document, applies each event with the rules, and keeps an undo history capped at 50 entries.

pub fn use_prose_editor(initial: impl FnOnce() -> EditDoc, rules: Rc<EditRules>) -> ProseEditor
pub fn use_prose_editor_with(
    initial: impl FnOnce() -> EditDoc,
    rules: Rc<EditRules>,
    validator: Option<Validator>,
    policy: StrictEdit,
) -> ProseEditorWith
pub fn use_validation(doc: &Rc<EditDoc>, validator: Option<&Validator>) -> Rc<Report>

use_prose_editor is use_prose_editor_with(initial, rules, None, StrictEdit::Flag).editor. use_validation recomputes the report only when the document or the validator is another one. Without a validator it returns the advisory report of no profile, which has no findings.

#[function_component(Editor)]
fn editor() -> Html {
    let rules = use_memo((), |_| EditRules::default());
    let config = use_memo((), |_| EditConfig::default());
    let editor = use_prose_editor(|| read_model(POLICY).unwrap_or_default(), rules);
    html! {
        <OdrlProseView doc={editor.doc.clone()} mode={EditMode::Edit}
                       config={config} onedit={editor.onedit.clone()} />
    }
}

From prose-yew/tests/site_examples.rs, region editor.

ProseEditor #

What use_prose_editor returns. Pass doc and onedit to OdrlProseView.

FieldTypeMeaning
docRc<EditDoc>The current document.
oneditCallback<EditEvent>Applies the event with the rules. A refused event leaves the document unchanged and sets last_error.
set_docCallback<EditDoc>Replace the document and clear the history.
undoCallback<()>Pop one history entry. Never gated.
can_undoboolThe history is not empty.
last_errorOption<EditError>Why the last event was refused.

ProseEditorWith #

What use_prose_editor_with returns. It is #[non_exhaustive]: do not build it with a struct literal.

FieldTypeMeaning
editorProseEditorAs above.
apply_allCallback<Vec<EditEvent>>Applies the events as one: all or none, one undo step. This is how a fix from a report is applied. An empty list is not an edit.
refusedOption<(EditEvent, EditError)>The last refused event and why, until the next edit succeeds or the validator or the StrictEdit policy changes. Hand error.issue(&event) to OdrlProseView::refusal.

StrictEdit #

What the reducer does with an event the loaded profiles do not allow. It is #[non_exhaustive].

After EditConfig::with_strict, give the hook Rc::new(cfg.rules.clone()) and cfg.validator.clone(). The reducer applies the rules it is handed, not the config's. A host that keeps EditRules::default() gets the defaults it set, not those with_strict made admissible.

EditConfig #

The whole configuration of an editing view. It is Clone, PartialEq and Default.

FieldTypeDefaultMeaning
rulesEditRulesEditRules::default()Limits per list. Controls the rules forbid are not rendered, and the reducer refuses the event anyway.
labelsLabelsLabels::default()Every word the editor writes. See Labels below.
classesProseClassesProseClasses::default()Extra classes per kind of element. See Styling.
stylesProseStylesProseStyles::default()Inline styles per kind of element.
vocabVocabularyVocabulary::default()Suggestions and closed lists: 20 ODRL actions, the known left operands, seven policy kinds, 12 operators, the conflict strategies.
add_buttonButtonContentButtonContent::SymbolAndTextWhat a plus button shows.
remove_buttonButtonContentButtonContent::SymbolWhat a minus button shows.
revealRevealReveal::AlwaysWhen the plus and minus buttons are visible.
base_cssbooltrueEdit mode writes <style> with BASE_CSS. Turn it off to bring your own.
show_raw_termsbooltrueOperator options read "is at most (lteq)" rather than "is at most".
suggestionsbooltrueA suggestion list while typing in a free-text slot. Off, a slot is a plain textbox.
max_suggestionsusize8The cap of the suggestion list.
host_plans_operator_switchesboolfalseThe host translates an operator switch the reducer would refuse, so the select offers every operator. Off, it offers only the switches the reducer accepts.
wordingRc<Wording>Rc::new(Wording::default())The loaded profiles. Set it with with_wording.
validatorOption<Validator>NoneSet it with with_strict. None validates nothing.

Two builders consume the config and return it:

pub fn with_wording(self, w: Rc<Wording>) -> EditConfig
pub fn with_strict(self, options: Options) -> EditConfig

with_wording uses the profiles and adds their terms to vocab under labels.group_profile, so set the labels first. with_strict validates against wording with options, usually Options::strict(), and rewrites rules.defaults (left operand, operator, action, policy type) so that what an add button creates is admissible. Call it after with_wording.

Choices that fix a name:

Labels #

Labels holds every word the editor writes: the section headings, the rule kinds, the empty states, the names of the buttons, the notes of the strict mode. Every field is public. Templates substitute {noun}, {n}, {field}, {owner}, {action}, {phrase} and {raw}, and in specific fields {profile}, {terms}, {note}, {kept}, {typed}, {label}, {suggestion}, {errors}, {warnings} and {notes}. When a button template has no {owner}, the owner chain is appended, so each button has a distinct name.

Keep the visible text of a button contained in its accessible name. A change of wording goes in a copy of Labels::default():

FieldDefaultGroup
conditionsApplies only ifSection headings
conditions_in_dutyApplies only ifSection headings
refinementsLimits on the action {action}Section headings
follow_up_dutyDuties to fulfil before this permission can be exercisedSection headings
follow_up_duty_elsewhereDuties attached to this ruleSection headings
follow_up_remedyRemedies if this prohibition is breachedSection headings
follow_up_consequenceConsequences if this duty is not fulfilledSection headings
follow_up_remedy_elsewhereRemedies attached to this ruleSection headings
follow_up_consequence_elsewhereConsequences attached to this ruleSection headings
carried_note(empty)Section headings
notes_headingNot renderedSection headings
maymayRule kinds
must_notmust notRule kinds
mustmustRule kinds
emptysee Nested wordingEmpty states
no_rulesNo rules yet.Empty states
no_policiesNo policies yet.Empty states
addAdd {noun}Button names
removeRemove {noun} {n}Button names
move_upMove {noun} {n} upButton names
move_downMove {noun} {n} downButton names
wrapGroup {noun} {n}Button names
unwrapUngroup {noun} {n}Button names
wrap_textGroupButton text
unwrap_textUngroupButton text
move_up_symbol↑Button text
move_down_symbol↓Button text
add_symbol+Button text
remove_symbol−Button text
operator_option{phrase}({raw})Selects, slots, announcements
unknown_option{raw}(not in the list)Selects, slots, announcements
slot_aria{field} of {owner}Selects, slots, announcements
rule_kind_ariaKind of {owner}Selects, slots, announcements
lockedKept exactly as written;not editable hereSelects, slots, announcements
inheritedFrom the policySelects, slots, announcements
announce_added{noun} addedSelects, slots, announcements
announce_removed{noun} removedSelects, slots, announcements
nounssee Nested wordingNested wording
fieldssee Nested wordingNested wording
placeholderssee Nested wordingNested wording
group_coreODRL coreProfiles and glossary
group_profileFrom profile:{profile}Profiles and glossary
glossary_headingTerms from the profileProfiles and glossary
kind_ofA kind of:{terms}Profiles and glossary
impliesImplies:{terms}Profiles and glossary
usage_noteUsage:{note}Profiles and glossary
from_profileFrom {profile}Profiles and glossary
deprecatedDeprecated in the profileProfiles and glossary
suggested_operatorsSuggested by the profileProfiles and glossary
other_operatorsOther operatorsProfiles and glossary
profile_valuesProfile values(not specific to this operand)Profiles and glossary
datatype_placeholdersa list of six pairs: integer, decimal, boolean, date, dateTime, durationProfiles and glossary
not_in_profile(not in the profile)Strict validation
kept_previousKept "{kept}";"{typed}" is not in the profileStrict validation
kept_empty"{typed}" is not in the profile;the field stays emptyStrict validation
allowed_groupAllowed by the profileStrict validation
not_allowed_groupNot allowed by the profileStrict validation
not_allowed_option{label}(not allowed)Strict validation
not_allowed_term"{typed}" is not an allowed {field} in {profile}.Strict validation
did_you_meanDid you mean {suggestion}?Strict validation
problems_headingProblemsStrict validation
go_toGo toStrict validation
status_conformsConformsStrict validation
status_violationsDoes not conformStrict validation
status_incompleteCannot be certified:part of the vocabulary was not checkedStrict validation
countserrors:{errors},warnings:{warnings},notes:{notes}Strict validation

Nested wording #

nouns, fields and placeholders are structs of text. empty holds the texts shown where a list is empty, whose defaults come from prose-core.

FieldDefault
Nouns.policypolicy
Nouns.permissionpermission
Nouns.prohibitionprohibition
Nouns.obligationobligation
Nouns.dutyduty
Nouns.remedyremedy
Nouns.consequenceconsequence
Nouns.assignerassigner
Nouns.assigneeassignee
Nouns.targettarget
Nouns.actionaction
Nouns.refinementlimit
Nouns.conditioncondition
Nouns.groupcondition group
Nouns.valuevalue
Nouns.profileprofile
Nouns.parentparent policy
FieldNames.kindpolicy type
FieldNames.uididentifier
FieldNames.conflictconflict strategy
FieldNames.profileprofile
FieldNames.inherit_fromparent policy
FieldNames.referencereference
FieldNames.iriidentifier
FieldNames.part_ofcollection
FieldNames.nameaction
FieldNames.left_operandleft operand
FieldNames.operatoroperator
FieldNames.right_operandvalue
FieldNames.operand_referencevalue reference
FieldNames.unitunit
FieldNames.logical_oplogical operator
Placeholders.kindtype
Placeholders.uididentifier
Placeholders.partyparty
Placeholders.assetasset
Placeholders.actionaction
Placeholders.left_operandleft operand
Placeholders.literalvalue
Placeholders.unitunit
Placeholders.operand_referenceIRI
Placeholders.part_ofcollection
Placeholders.profileprofile
Placeholders.policy_refpolicy identifier
Placeholders.referenceIRI
EmptyTexts.anyone_subjectAnyone
EmptyTexts.anyone_objectanyone
EmptyTexts.the_assigneeThe assignee
EmptyTexts.unspecified_assetan unspecified asset
EmptyTexts.unspecified_actionperform an unspecified action on
EmptyTexts.no_assignerno assigner
EmptyTexts.no_parentno other policy
EmptyTexts.no_profileno profile

Examples #

Every block above comes from prose-yew/tests/site_examples.rs. The remaining two are the configuration with your own CSS, which Styling explains, and the strict editor with the problems panel, which Validation explains.

The demo in demo/ is the full host: demo/src/edit.rs adds undo, the strict bar, issues and onjump.