diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index b45f26cb5..946971bf8 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -818,7 +818,8 @@ }, "inherited_attribute_list": { "title": "Inherited Attributes", - "no_inherited_attributes": "No inherited attributes." + "no_inherited_attributes": "No inherited attributes.", + "none": "none" }, "note_info_widget": { "note_id": "Note ID", @@ -2203,6 +2204,9 @@ "note_paths_title": "Note paths", "code_note_switcher": "Change language mode" }, + "attributes_panel": { + "title": "Note Attributes" + }, "right_pane": { "empty_message": "Nothing to show for this note", "empty_button": "Hide the panel", diff --git a/apps/client/src/widgets/layout/StatusBar.css b/apps/client/src/widgets/layout/StatusBar.css index 8a21c233d..0663d6083 100644 --- a/apps/client/src/widgets/layout/StatusBar.css +++ b/apps/client/src/widgets/layout/StatusBar.css @@ -244,20 +244,44 @@ > .attribute-list { font-size: 0.9em; - .inherited-attributes-widget > div { - padding: 0; - font-size: 0.9em; + .attributes-panel-label { + opacity: .5; + margin-inline-end: 4px; + font-weight: 600; } - .attribute-list-editor { - padding-block: 0 !important; - padding-inline: 0 100px !important ; - } + .inherited-attributes-widget { + display: inline; + + > div { + display: inline; + padding: 0; + } + } + + .attribute-list-editor-wrapper { + display: flex; + flex-direction: column-reverse; + padding-bottom: 0 !important; + + .attribute-list-editor { + padding-block: 0 !important; + padding-inline: 0 100px !important ; + } + + .attribute-errors { + padding: 4px 0; + color: var(--dropdown-item-icon-destructive-color); + font-style: italic; + } + + .ck.ck-editor__editable::after { + /* Remove a hidden spinner that causes overflow */ + display: none; + } + } + - .ck.ck-editor__editable::after { - /* Remove a hidden spinner that causes overflow */ - display: none; - } } div.similar-notes-widget div.similar-notes-wrapper { diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx index 691b8c576..932d51b53 100644 --- a/apps/client/src/widgets/layout/StatusBar.tsx +++ b/apps/client/src/widgets/layout/StatusBar.tsx @@ -268,7 +268,7 @@ function NoteInfoValue({ text, title, value }: { text: string; title?: string, v function SimilarNotesPane({ note, similarNotesShown, setSimilarNotesShown }: NoteInfoContext) { return (similarNotesShown && - - - + + {t("inherited_attribute_list.title")} + ) { +type InheritedAttributesTabArgs = Pick & { + emptyListString?: string; +} + +export default function InheritedAttributesTab({ note, componentId, emptyListString }: InheritedAttributesTabArgs) { const [ inheritedAttributes, setInheritedAttributes ] = useState(); const [ attributeDetailWidgetEl, attributeDetailWidget ] = useLegacyWidget(() => new AttributeDetailWidget()); @@ -63,7 +67,7 @@ export default function InheritedAttributesTab({ note, componentId }: Pick )), " ") ) : ( - <>{t("inherited_attribute_list.no_inherited_attributes")} + <>{t(emptyListString ?? "inherited_attribute_list.no_inherited_attributes")} )} diff --git a/apps/client/src/widgets/ribbon/components/AttributeEditor.tsx b/apps/client/src/widgets/ribbon/components/AttributeEditor.tsx index ee9129bbd..3871e0790 100644 --- a/apps/client/src/widgets/ribbon/components/AttributeEditor.tsx +++ b/apps/client/src/widgets/ribbon/components/AttributeEditor.tsx @@ -283,6 +283,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI return ( <> {!hidden &&
{ @@ -296,106 +297,107 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI setTimeout(() => save(), 100); } }} - > - { - currentValueRef.current = currentValue ?? ""; - - const oldValue = getPreprocessedData(lastSavedContent.current ?? "").trimEnd(); - const newValue = getPreprocessedData(currentValue ?? "").trimEnd(); - setNeedsSaving(oldValue !== newValue); - setError(undefined); - }} - onClick={(e, pos) => { - if (pos && pos.textNode && pos.textNode.data) { - const clickIndex = getClickIndex(pos); - - let parsedAttrs: Attribute[]; - - try { - parsedAttrs = attribute_parser.lexAndParse(getPreprocessedData(currentValueRef.current), true); - } catch (e: unknown) { - // the input is incorrect because the user messed up with it and now needs to fix it manually - console.log(e); - return null; - } - - let matchedAttr: Attribute | null = null; - - for (const attr of parsedAttrs) { - if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) { - matchedAttr = attr; - break; - } - } - - setTimeout(() => { - if (matchedAttr) { - attributeDetailWidget.showAttributeDetail({ - allAttributes: parsedAttrs, - attribute: matchedAttr, - isOwned: true, - x: e.pageX, - y: e.pageY - }); - setState("showAttributeDetail"); - } else { - setState("showHelpTooltip"); - } - }, 100); - } else { - setState("showHelpTooltip"); - } - }} - onKeyDown={() => attributeDetailWidget.hide()} - onBlur={() => save()} - onInitialized={() => editorRef.current?.focus()} - disableNewlines disableSpellcheck - /> - -
- { needsSaving && } - - { - // Prevent automatic hiding of the context menu due to the button being clicked. - e.stopPropagation(); - - contextMenu.show({ - x: e.pageX, - y: e.pageY, - orientation: "left", - items: [ - { title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" }, - { title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" }, - { kind: "separator" }, - { title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" }, - { title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" } - ], - selectMenuItemHandler: (item) => handleAddNewAttributeCommand(item.command) - }); + >
+ { + currentValueRef.current = currentValue ?? ""; + + const oldValue = getPreprocessedData(lastSavedContent.current ?? "").trimEnd(); + const newValue = getPreprocessedData(currentValue ?? "").trimEnd(); + setNeedsSaving(oldValue !== newValue); + setError(undefined); + }} + onClick={(e, pos) => { + if (pos && pos.textNode && pos.textNode.data) { + const clickIndex = getClickIndex(pos); + + let parsedAttrs: Attribute[]; + + try { + parsedAttrs = attribute_parser.lexAndParse(getPreprocessedData(currentValueRef.current), true); + } catch (e: unknown) { + // the input is incorrect because the user messed up with it and now needs to fix it manually + console.log(e); + return null; + } + + let matchedAttr: Attribute | null = null; + + for (const attr of parsedAttrs) { + if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) { + matchedAttr = attr; + break; + } + } + + setTimeout(() => { + if (matchedAttr) { + attributeDetailWidget.showAttributeDetail({ + allAttributes: parsedAttrs, + attribute: matchedAttr, + isOwned: true, + x: e.pageX, + y: e.pageY + }); + setState("showAttributeDetail"); + } else { + setState("showHelpTooltip"); + } + }, 100); + } else { + setState("showHelpTooltip"); + } + }} + onKeyDown={() => attributeDetailWidget.hide()} + onBlur={() => save()} + onInitialized={() => editorRef.current?.focus()} + disableNewlines disableSpellcheck /> + +
+ { needsSaving && } + + { + // Prevent automatic hiding of the context menu due to the button being clicked. + e.stopPropagation(); + + contextMenu.show({ + x: e.pageX, + y: e.pageY, + orientation: "left", + items: [ + { title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" }, + { title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" }, + { kind: "separator" }, + { title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" }, + { title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" } + ], + selectMenuItemHandler: (item) => handleAddNewAttributeCommand(item.command) + }); + }} + /> +
{ error && (