diff --git a/apps/client/src/widgets/PromotedAttributes.css b/apps/client/src/widgets/PromotedAttributes.css
new file mode 100644
index 000000000..ea95fd455
--- /dev/null
+++ b/apps/client/src/widgets/PromotedAttributes.css
@@ -0,0 +1,87 @@
+body.mobile .promoted-attributes-widget {
+ /* https://github.com/zadam/trilium/issues/4468 */
+ flex-shrink: 0.4;
+ overflow: auto;
+}
+
+.promoted-attributes-container {
+ margin: 0 1.5em;
+ overflow: auto;
+ max-height: 400px;
+ flex-wrap: wrap;
+ display: table;
+}
+.promoted-attribute-cell {
+ display: flex;
+ align-items: center;
+ margin: 10px;
+ display: table-row;
+}
+.promoted-attribute-cell > label {
+ user-select: none;
+ font-weight: bold;
+ vertical-align: middle;
+}
+.promoted-attribute-cell > * {
+ display: table-cell;
+ padding: 1px 0;
+}
+
+.promoted-attribute-cell div.input-group {
+ margin-inline-start: 10px;
+ display: flex;
+ min-height: 40px;
+}
+.promoted-attribute-cell strong {
+ word-break:keep-all;
+ white-space: nowrap;
+}
+
+.promoted-attribute-cell input[type="checkbox"] {
+ width: 22px !important;
+ flex-grow: 0;
+ width: unset;
+}
+
+/* Restore default apperance */
+.promoted-attribute-cell input[type="number"],
+.promoted-attribute-cell input[type="checkbox"] {
+ appearance: auto;
+}
+
+.promoted-attribute-cell input[type="color"] {
+ width: 24px;
+ height: 24px;
+ margin-top: 2px;
+ appearance: none;
+ padding: 0;
+ border: 0;
+ outline: none;
+ border-radius: 25% !important;
+}
+
+.promoted-attribute-cell input[type="color"]::-webkit-color-swatch-wrapper {
+ padding: 0;
+}
+
+.promoted-attribute-cell input[type="color"]::-webkit-color-swatch {
+ border: none;
+ border-radius: 25%;
+}
+
+.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"] {
+ position: relative;
+ opacity: 0.5;
+}
+
+.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"]:after {
+ content: "";
+ position: absolute;
+ top: 10px;
+ inset-inline-start: 0px;
+ inset-inline-end: 0;
+ height: 2px;
+ background: rgba(0, 0, 0, 0.5);
+ transform: rotate(45deg);
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/PromotedAttributes.tsx b/apps/client/src/widgets/PromotedAttributes.tsx
index 1a597b281..58ed87336 100644
--- a/apps/client/src/widgets/PromotedAttributes.tsx
+++ b/apps/client/src/widgets/PromotedAttributes.tsx
@@ -1,3 +1,51 @@
+import { useEffect, useState } from "preact/hooks";
+import "./PromotedAttributes.css";
+import { useNoteContext } from "./react/hooks";
+import { Attribute } from "../services/attribute_parser";
+
export default function PromotedAttributes() {
- return
Promoted attributes go here.
+ const { note } = useNoteContext();
+ const [ cells, setCells ] = useState();
+
+ useEffect(() => {
+ if (!note) return;
+ const promotedDefAttrs = note.getPromotedDefinitionAttributes();
+ const ownedAttributes = note.getOwnedAttributes();
+ // attrs are not resorted if position changes after the initial load
+ // promoted attrs are sorted primarily by order of definitions, but with multi-valued promoted attrs
+ // the order of attributes is important as well
+ ownedAttributes.sort((a, b) => a.position - b.position);
+
+ const cells: Attribute[] = [];
+ for (const definitionAttr of promotedDefAttrs) {
+ const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation";
+ const valueName = definitionAttr.name.substr(valueType.length + 1);
+
+ let valueAttrs = ownedAttributes.filter((el) => el.name === valueName && el.type === valueType) as Attribute[];
+
+ if (valueAttrs.length === 0) {
+ valueAttrs.push({
+ attributeId: "",
+ type: valueType,
+ name: valueName,
+ value: ""
+ });
+ }
+
+ if (definitionAttr.getDefinition().multiplicity === "single") {
+ valueAttrs = valueAttrs.slice(0, 1);
+ }
+
+ cells.push(...valueAttrs);
+ }
+ setCells(cells);
+ }, [ note ]);
+
+ return (
+
+ );
}
diff --git a/apps/client/src/widgets/promoted_attributes.ts b/apps/client/src/widgets/promoted_attributes.ts
index 5853e1d87..7620bfa4b 100644
--- a/apps/client/src/widgets/promoted_attributes.ts
+++ b/apps/client/src/widgets/promoted_attributes.ts
@@ -12,102 +12,6 @@ import type { Attribute } from "../services/attribute_parser.js";
import type FAttribute from "../entities/fattribute.js";
import type { EventData } from "../components/app_context.js";
-const TPL = /*html*/`
-`;
-
// TODO: Deduplicate
interface AttributeResult {
attributeId: string;
@@ -126,36 +30,12 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
}
doRender() {
- this.$widget = $(TPL);
this.contentSized();
- this.$container = this.$widget.find(".promoted-attributes-container");
- }
-
- getTitle(note: FNote) {
- const promotedDefAttrs = note.getPromotedDefinitionAttributes();
-
- if (promotedDefAttrs.length === 0) {
- return { show: false };
- }
-
- return {
- show: true,
- activate: options.is("promotedAttributesOpenInRibbon"),
- title: t("promoted_attributes.promoted_attributes"),
- icon: "bx bx-table"
- };
}
async refreshWithNote(note: FNote) {
this.$container.empty();
- const promotedDefAttrs = note.getPromotedDefinitionAttributes();
- const ownedAttributes = note.getOwnedAttributes();
- // attrs are not resorted if position changes after the initial load
- // promoted attrs are sorted primarily by order of definitions, but with multi-valued promoted attrs
- // the order of attributes is important as well
- ownedAttributes.sort((a, b) => a.position - b.position);
-
if (promotedDefAttrs.length === 0 || note.getLabelValue("viewType") === "table") {
this.toggleInt(false);
return;
@@ -163,33 +43,13 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
const $cells: JQuery[] = [];
- for (const definitionAttr of promotedDefAttrs) {
- const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation";
- const valueName = definitionAttr.name.substr(valueType.length + 1);
-
- let valueAttrs = ownedAttributes.filter((el) => el.name === valueName && el.type === valueType) as Attribute[];
-
- if (valueAttrs.length === 0) {
- valueAttrs.push({
- attributeId: "",
- type: valueType,
- name: valueName,
- value: ""
- });
- }
-
- if (definitionAttr.getDefinition().multiplicity === "single") {
- valueAttrs = valueAttrs.slice(0, 1);
- }
-
- for (const valueAttr of valueAttrs) {
+ for (const valueAttr of valueAttrs) {
const $cell = await this.createPromotedAttributeCell(definitionAttr, valueAttr, valueName);
if ($cell) {
$cells.push($cell);
}
}
- }
// we replace the whole content in one step, so there can't be any race conditions
// (previously we saw promoted attributes doubling)