From 8211cbab9f38b702cd69fbf478cb7026d894045b Mon Sep 17 00:00:00 2001 From: Maxim <45532431+42ama@users.noreply.github.com> Date: Sat, 27 Apr 2024 22:05:57 +0400 Subject: [PATCH 1/5] #937 Mulit-Tag sorting feature (#944) * #937 Mulit-Tag sorting feature + Added new Column property - contentsSortDir, it stores information about sorting the content in the cells of the column. At the moment the only type that can store several items in a cell is MultiTag, so the processing logic was added only for it. + Added a options submenu for columns with MultiTag type, where you can select the sorting type for the cell contents. * Renamed `contentsSortDir` to `multiTagSortDir` and `Contents sorting` to `Sort` --- manifest.json | 2 +- package.json | 2 +- src/data/serialize-state.ts | 14 +- .../loom-app/body-cell-container/index.tsx | 8 +- .../loom-app/header-menu/base-submenu.tsx | 3 +- src/react/loom-app/header-menu/index.tsx | 18 + .../header-menu/multitag-sort-dir-submenu.tsx | 40 ++ .../loom-app/header-menu/option-submenu.tsx | 12 + src/react/loom-app/header-menu/types.ts | 1 + src/react/loom-app/table/index.tsx | 2 + src/shared/frontmatter/index.ts | 2 + src/shared/loom-state/loom-state-factory.ts | 5 +- src/shared/loom-state/migrate/index.ts | 2 + .../loom-state/migrate/migrate-state-22.ts | 28 + src/shared/loom-state/types/index.ts | 1 + src/shared/loom-state/types/loom-state-21.ts | 485 ++++++++++++++++++ src/shared/loom-state/types/loom-state.ts | 6 +- src/shared/loom-state/validate-state.ts | 1 + versions.json | 3 +- 19 files changed, 625 insertions(+), 10 deletions(-) create mode 100644 src/react/loom-app/header-menu/multitag-sort-dir-submenu.tsx create mode 100644 src/shared/loom-state/migrate/migrate-state-22.ts create mode 100644 src/shared/loom-state/types/loom-state-21.ts diff --git a/manifest.json b/manifest.json index 5df590a8..057f8d81 100644 --- a/manifest.json +++ b/manifest.json @@ -9,5 +9,5 @@ "fundingUrl": { "Buymeacoffee": "https://www.buymeacoffee.com/treywallis" }, - "version": "8.15.12" + "version": "8.15.13" } diff --git a/package.json b/package.json index 5f021044..a831cea3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-dataloom", - "version": "8.15.12", + "version": "8.15.13", "description": "Weave together data from diverse sources into different views. Inspired by Excel Spreadsheets and Notion.so.", "main": "main.js", "scripts": { diff --git a/src/data/serialize-state.ts b/src/data/serialize-state.ts index 619ddfeb..26b1a896 100644 --- a/src/data/serialize-state.ts +++ b/src/data/serialize-state.ts @@ -26,6 +26,7 @@ import { LoomState18, LoomState19, LoomState20, + LoomState21, } from "src/shared/loom-state/types"; import { @@ -50,10 +51,11 @@ import { MigrateState18, MigrateState19, MigrateState20, + MigrateState21, + MigrateState22, } from "src/shared/loom-state/migrate"; import { LoomStateObject } from "src/shared/loom-state/validate-state"; import DeserializationError from "./deserialization-error"; -import MigrateState21 from "src/shared/loom-state/migrate/migrate-state-21"; export const serializeState = (state: LoomState): string => { //Filter out any source rows, as these are populated by the plugin @@ -316,6 +318,16 @@ export const deserializeState = ( failedMigration = null; } + const VERSION_8_15_13 = "8.15.13"; + if (isVersionLessThan(fileVersion, VERSION_8_15_13)) { + failedMigration = VERSION_8_15_13; + const nextState = new MigrateState22().migrate( + currentState as LoomState21 + ); + currentState = nextState; + failedMigration = null; + } + //TODO handle previous versions? LoomStateObject.check(currentState); diff --git a/src/react/loom-app/body-cell-container/index.tsx b/src/react/loom-app/body-cell-container/index.tsx index 59bf64ba..61eeeb86 100644 --- a/src/react/loom-app/body-cell-container/index.tsx +++ b/src/react/loom-app/body-cell-container/index.tsx @@ -71,6 +71,8 @@ import { getFileCellContent } from "src/shared/cell-content/file-cell-content"; import { getDateCellContent } from "src/shared/cell-content/date-cell-content"; import DisabledCell from "../disabled-cell"; +import { sortByText } from "src/shared/sort-utils"; + interface BaseCellProps { frontmatterKey: string | null; aspectRatio: AspectRatio; @@ -440,9 +442,11 @@ export default function BodyCellContainer(props: Props) { } }; } else { - const { tagIds } = props as MultiTagCellProps; - cellTags = columnTags.filter((tag) => tagIds.includes(tag.id)); + const { tagIds, multiTagSortDir } = props as MultiTagCellProps; + cellTags = columnTags.filter((tag) => tagIds.includes(tag.id)); + cellTags.sort((a, b) => sortByText(a.content, b.content, multiTagSortDir, false)); + handleMenuTriggerBackspaceDown = () => { onTagCellMultipleRemove(id, tagIds); }; diff --git a/src/react/loom-app/header-menu/base-submenu.tsx b/src/react/loom-app/header-menu/base-submenu.tsx index 28c61a63..d985dd88 100644 --- a/src/react/loom-app/header-menu/base-submenu.tsx +++ b/src/react/loom-app/header-menu/base-submenu.tsx @@ -70,7 +70,8 @@ export default function BaseSubmenu({ columnType === CellType.EMBED || columnType === CellType.NUMBER || columnType === CellType.LAST_EDITED_TIME || - columnType === CellType.CREATION_TIME; + columnType === CellType.CREATION_TIME|| + columnType === CellType.MULTI_TAG; return ( diff --git a/src/react/loom-app/header-menu/index.tsx b/src/react/loom-app/header-menu/index.tsx index 1654c244..abd80008 100644 --- a/src/react/loom-app/header-menu/index.tsx +++ b/src/react/loom-app/header-menu/index.tsx @@ -12,6 +12,7 @@ import FrontmatterKeySubmenu from "./frontmatter-key-submenu"; import BaseSubmenu from "./base-submenu"; import DateFormatSeparatorSubmenu from "./date-format-separator-submenu"; import TimeFormatSubmenu from "./time-format-submenu"; +import MultiTagSortDirSubmenu from "./multitag-sort-dir-submenu"; import { AspectRatio, @@ -88,6 +89,7 @@ export default function HeaderMenu({ numberSeparator, numberSuffix, frontmatterKey, + multiTagSortDir } = column; const [submenu, setSubmenu] = React.useState(null); const [localValue, setLocalValue] = React.useState(content); @@ -241,6 +243,13 @@ export default function HeaderMenu({ onClose(); } + function handleMultiTagSortDirClick(value: SortDir) { + onColumnChange(columnId, { multiTagSortDir: value }); + saveLocalValue(); + onClose(); + setSubmenu(SubmenuType.OPTIONS); + } + return (
@@ -280,6 +289,7 @@ export default function HeaderMenu({ numberPrefix={numberPrefix} numberSuffix={numberSuffix} numberSeparator={numberSeparator} + multiTagSortDir={multiTagSortDir} onBackClick={() => setSubmenu(null)} onSubmenuChange={setSubmenu} /> @@ -390,6 +400,14 @@ export default function HeaderMenu({ onBackClick={() => setSubmenu(null)} /> )} + {submenu === SubmenuType.CONTENTS_SORT_DIR && ( + setSubmenu(null)} + /> + )}
); diff --git a/src/react/loom-app/header-menu/multitag-sort-dir-submenu.tsx b/src/react/loom-app/header-menu/multitag-sort-dir-submenu.tsx new file mode 100644 index 00000000..41402291 --- /dev/null +++ b/src/react/loom-app/header-menu/multitag-sort-dir-submenu.tsx @@ -0,0 +1,40 @@ +import MenuItem from "src/react/shared/menu-item"; +import Submenu from "../../shared/submenu"; +import { SortDir } from "src/shared/loom-state/types/loom-state"; + +interface Props { + title: string; + value: SortDir; + onValueClick: (value: SortDir) => void; + onBackClick: () => void; +} + +export default function MultiTagSortDirSubmenu({ + title, + value, + onValueClick, + onBackClick, +}: Props) { + return ( + + onValueClick(SortDir.ASC)} + isSelected={value === SortDir.ASC} + /> + onValueClick(SortDir.DESC)} + isSelected={value === SortDir.DESC} + /> + onValueClick(SortDir.NONE)} + isSelected={value === SortDir.NONE} + /> + + ); +} diff --git a/src/react/loom-app/header-menu/option-submenu.tsx b/src/react/loom-app/header-menu/option-submenu.tsx index ea09c084..337849a4 100644 --- a/src/react/loom-app/header-menu/option-submenu.tsx +++ b/src/react/loom-app/header-menu/option-submenu.tsx @@ -8,6 +8,7 @@ import { DateFormatSeparator, NumberFormat, PaddingSize, + SortDir } from "src/shared/loom-state/types/loom-state"; import Stack from "src/react/shared/stack"; import Padding from "src/react/shared/padding"; @@ -33,6 +34,7 @@ interface Props { verticalPadding: PaddingSize; horizontalPadding: PaddingSize; aspectRatio: AspectRatio; + multiTagSortDir: SortDir; onBackClick: () => void; onSubmenuChange: (value: SubmenuType) => void; } @@ -51,6 +53,7 @@ export default function OptionSubmenu({ horizontalPadding, title, dateFormat, + multiTagSortDir, onBackClick, onSubmenuChange, }: Props) { @@ -165,6 +168,15 @@ export default function OptionSubmenu({ /> )} + {type === CellType.MULTI_TAG && ( + + onSubmenuChange(SubmenuType.CONTENTS_SORT_DIR) + } + /> + )}
diff --git a/src/react/loom-app/header-menu/types.ts b/src/react/loom-app/header-menu/types.ts index befef0ce..9f3f02d5 100644 --- a/src/react/loom-app/header-menu/types.ts +++ b/src/react/loom-app/header-menu/types.ts @@ -12,4 +12,5 @@ export enum SubmenuType { TEXT_INPUT_NUMBER_SUFFIX, TEXT_INPUT_NUMBER_SEPARATOR, FRONTMATTER_KEY, + CONTENTS_SORT_DIR } diff --git a/src/react/loom-app/table/index.tsx b/src/react/loom-app/table/index.tsx index fee70c0f..de22799a 100644 --- a/src/react/loom-app/table/index.tsx +++ b/src/react/loom-app/table/index.tsx @@ -358,6 +358,7 @@ const Table = React.forwardRef(function Table( horizontalPadding, aspectRatio, frontmatterKey, + multiTagSortDir } = column; const cell = row.cells.find( @@ -457,6 +458,7 @@ const Table = React.forwardRef(function Table( {...commonProps} type={type} tagIds={tagIds} + multiTagSortDir={multiTagSortDir} onCellChange={onCellChange} onTagAdd={onTagAdd} onTagCellAdd={onTagCellAdd} diff --git a/src/shared/frontmatter/index.ts b/src/shared/frontmatter/index.ts index 2b3c6fb4..9c998043 100644 --- a/src/shared/frontmatter/index.ts +++ b/src/shared/frontmatter/index.ts @@ -213,6 +213,7 @@ export const deserializeFrontmatterForCell = ( return { newCell: createMultiTagCell(id, { hasValidFrontmatter: false, + multiTagSortDir: column.multiTagSortDir }), }; } @@ -238,6 +239,7 @@ export const deserializeFrontmatterForCell = ( const newCell = createMultiTagCell(id, { tagIds: cellTagIds, hasValidFrontmatter: true, + multiTagSortDir: column.multiTagSortDir }); const nextTags = [...column.tags, ...newTags]; return { diff --git a/src/shared/loom-state/loom-state-factory.ts b/src/shared/loom-state/loom-state-factory.ts index 1cdfc1a2..5bffc795 100644 --- a/src/shared/loom-state/loom-state-factory.ts +++ b/src/shared/loom-state/loom-state-factory.ts @@ -152,6 +152,7 @@ export const createColumn = (options?: { horizontalPadding: PaddingSize.UNSET, verticalPadding: PaddingSize.UNSET, frontmatterKey, + multiTagSortDir: SortDir.NONE }; }; @@ -355,14 +356,16 @@ export const createMultiTagCell = ( options?: { tagIds?: string[]; hasValidFrontmatter?: boolean; + multiTagSortDir?: SortDir; } ): MultiTagCell => { - const { tagIds = [], hasValidFrontmatter = null } = options || {}; + const { tagIds = [], hasValidFrontmatter = null, multiTagSortDir = SortDir.NONE } = options || {}; return { id: generateUuid(), columnId, tagIds, hasValidFrontmatter, + multiTagSortDir }; }; diff --git a/src/shared/loom-state/migrate/index.ts b/src/shared/loom-state/migrate/index.ts index fd4dc3fc..1aaadc11 100644 --- a/src/shared/loom-state/migrate/index.ts +++ b/src/shared/loom-state/migrate/index.ts @@ -19,3 +19,5 @@ export { default as MigrateState17 } from "./migrate-state-17"; export { default as MigrateState18 } from "./migrate-state-18"; export { default as MigrateState19 } from "./migrate-state-19"; export { default as MigrateState20 } from "./migrate-state-20"; +export { default as MigrateState21 } from "./migrate-state-21"; +export { default as MigrateState22 } from "./migrate-state-22"; diff --git a/src/shared/loom-state/migrate/migrate-state-22.ts b/src/shared/loom-state/migrate/migrate-state-22.ts new file mode 100644 index 00000000..9030d293 --- /dev/null +++ b/src/shared/loom-state/migrate/migrate-state-22.ts @@ -0,0 +1,28 @@ +import MigrateState from "./migrate-state"; +import { LoomState20, LoomState } from "../types"; +import { Row, SortDir, Column } from "../types/loom-state"; +import { LoomState21 } from "../types/loom-state-21"; + +/** + * Migrates to 8.15.13 + */ +export default class MigrateState22 implements MigrateState { + public migrate(prevState: LoomState21): LoomState { + const { columns } = prevState.model; + + const nextColumns: Column[] = columns.map((column) => { + return { + ...column, + multiTagSortDir: SortDir.NONE, + }; + }); + + return { + ...prevState, + model: { + ...prevState.model, + columns: nextColumns, + }, + }; + } +} diff --git a/src/shared/loom-state/types/index.ts b/src/shared/loom-state/types/index.ts index 7e2e5ae2..0d1fd3fc 100644 --- a/src/shared/loom-state/types/index.ts +++ b/src/shared/loom-state/types/index.ts @@ -20,3 +20,4 @@ export type { LoomState17 } from "./loom-state-17"; export type { LoomState18 } from "./loom-state-18"; export type { LoomState19 } from "./loom-state-19"; export type { LoomState20 } from "./loom-state-20"; +export type { LoomState21 } from "./loom-state-21"; diff --git a/src/shared/loom-state/types/loom-state-21.ts b/src/shared/loom-state/types/loom-state-21.ts new file mode 100644 index 00000000..42638375 --- /dev/null +++ b/src/shared/loom-state/types/loom-state-21.ts @@ -0,0 +1,485 @@ +/******* Type definitions for v8.15.6 *******/ + +import { ObsidianPropertyType } from "src/shared/frontmatter/types"; + +/** + * v8.15.6 + */ +export interface LoomState21 { + pluginVersion: string; + model: TableModel; +} + +interface TableModel { + columns: Column[]; + rows: Row[]; + filters: Filter[]; + settings: TableSettings; + sources: Source[]; + externalRowOrder: ExternalRowOrder[]; +} + +interface ExternalRowOrder { + sourceId: string; + index: number; + uniqueId: string; //This could be a file path, tag name, or url +} + +interface TableSettings { + numFrozenColumns: number; + showCalculationRow: boolean; +} + +interface Column { + id: string; + sortDir: SortDir; + width: string; + type: CellType; + isVisible: boolean; + dateFormat: DateFormat; + dateFormatSeparator: DateFormatSeparator; + hour12: boolean; + includeTime: boolean; + content: string; + numberFormat: NumberFormat; + currencyType: CurrencyType; + numberPrefix: string; + numberSuffix: string; + numberSeparator: string; + shouldWrapOverflow: boolean; + tags: Tag[]; + calculationType: CalculationType; + aspectRatio: AspectRatio; + horizontalPadding: PaddingSize; + verticalPadding: PaddingSize; + frontmatterKey: string | null; +} + +interface Row { + id: string; + index: number; + creationDateTime: string; + lastEditedDateTime: string; + sourceId: string | null; + cells: Cell[]; +} + +interface BaseCell { + id: string; + columnId: string; + hasValidFrontmatter: boolean | null; +} + +interface TextCell extends BaseCell { + content: string; +} + +interface EmbedCell extends BaseCell { + isExternal: boolean; + pathOrUrl: string; + alias: string | null; +} + +interface EmbedUrl { + type: "url"; + url: string; +} + +interface EmbedPath { + type: "path"; + path: string; + alias: string | null; +} + +interface FileCell extends BaseCell { + path: string; + alias: string | null; //We save an alias to be able to serialize into the frontmatter +} + +interface CheckboxCell extends BaseCell { + value: boolean; +} + +interface TagCell extends BaseCell { + tagId: string | null; +} + +interface MultiTagCell extends BaseCell { + tagIds: string[]; +} + +interface NumberCell extends BaseCell { + value: number | null; +} + +interface DateCell extends BaseCell { + dateTime: string | null; +} + +type CreationTimeCell = BaseCell; + +type LastEditedTimeCell = BaseCell; + +type SourceCell = BaseCell; + +interface SourceFileCell extends BaseCell { + path: string; +} + +type Cell = + | TextCell + | EmbedCell + | FileCell + | CheckboxCell + | TagCell + | MultiTagCell + | NumberCell + | DateCell + | CreationTimeCell + | LastEditedTimeCell + | SourceCell + | SourceFileCell; + +interface Tag { + id: string; + content: string; + color: Color; +} + +enum Color { + LIGHT_GRAY = "light gray", + GRAY = "gray", + BROWN = "brown", + ORANGE = "orange", + YELLOW = "yellow", + GREEN = "green", + BLUE = "blue", + PURPLE = "purple", + PINK = "pink", + RED = "red", +} + +enum PaddingSize { + UNSET = "unset", + SM = "sm", + MD = "md", + LG = "lg", + XL = "xl", + XXL = "2xl", + XXXL = "3xl", + XXXXL = "4xl", +} + +enum SortDir { + ASC = "asc", + DESC = "desc", + NONE = "default", +} + +enum CellType { + SOURCE_FILE = "source-file", + SOURCE = "source", + TEXT = "text", + EMBED = "embed", + FILE = "file", + NUMBER = "number", + TAG = "tag", + MULTI_TAG = "multi-tag", + DATE = "date", + CHECKBOX = "checkbox", + CREATION_TIME = "creation-time", + LAST_EDITED_TIME = "last-edited-time", +} + +enum DateFormat { + MM_DD_YYYY = "mmddyyyy", + DD_MM_YYYY = "ddmmyyyy", + YYYY_MM_DD = "yyyymmdd", +} + +enum DateFormatSeparator { + HYPHEN = "-", + SLASH = "/", + DOT = ".", +} + +enum NumberFormat { + NUMBER = "number", + CURRENCY = "currency", +} + +enum CurrencyType { + ARGENTINA = "ARS", + AUSTRALIA = "AUD", + CANADA = "CAD", + COLOMBIA = "COP", + DENMARK = "DKK", + UAE = "AED", + EUROPE = "EUR", + ICELAND = "ISK", + ISRAEL = "ILS", + MEXICO = "MXN", + NORWAY = "NOK", + GREAT_BRITAIN = "GBP", + BRAZIL = "BRL", + SAUDI_ARABIA = "SAR", + RUSSIA = "RUB", + INDIA = "INR", + SINGAPORE = "SGB", + SWEDEN = "SEK", + SWITZERLAND = "CHF", + UNITED_STATES = "USD", + SOUTH_KOREA = "KRW", + JAPAN = "JPY", + CHINA = "CNY", +} + +enum AspectRatio { + UNSET = "unset", + NINE_BY_SIXTEEN = "9/16", + FOUR_BY_THREE = "4/3", + SIXTEEN_BY_NINE = "16/9", +} + +/********** CALCULATIONS **********/ +enum GeneralCalculation { + NONE = "none", + COUNT_ALL = "count-all", + COUNT_VALUES = "count-values", + COUNT_UNIQUE = "count-unique", + COUNT_EMPTY = "count-empty", + COUNT_NOT_EMPTY = "count-not-empty", + PERCENT_EMPTY = "percent-empty", + PERCENT_NOT_EMPTY = "percent-not-empty", +} + +enum NumberCalculation { + SUM = "sum", + AVG = "avg", + MIN = "min", + MAX = "max", + MEDIAN = "median", + RANGE = "range", +} + +type CalculationType = GeneralCalculation | NumberCalculation; + +/************* FILTERS ****************/ +type FilterOperator = "and" | "or"; + +enum TextFilterCondition { + IS = "is", + IS_NOT = "is-not", + CONTAINS = "contains", + DOES_NOT_CONTAIN = "does-not-contain", + STARTS_WITH = "starts-with", + ENDS_WITH = "ends-with", + IS_EMPTY = "is-empty", + IS_NOT_EMPTY = "is-not-empty", +} + +enum NumberFilterCondition { + IS_EQUAL = "is-equal", + IS_NOT_EQUAL = "is-not-equal", + IS_GREATER = "is-greater", + IS_LESS = "is-less", + IS_GREATER_OR_EQUAL = "is-greater-or-equal", + IS_LESS_OR_EQUAL = "is-less-or-equal", + IS_EMPTY = "is-empty", + IS_NOT_EMPTY = "is-not-empty", +} + +//TODO add support for more date types +enum DateFilterCondition { + IS = "is", + IS_BEFORE = "is-before", + IS_AFTER = "is-after", + IS_EMPTY = "is-empty", + IS_NOT_EMPTY = "is-not-empty", +} + +enum DateFilterOption { + UNSELECTED = "unselected", + TODAY = "today", + TOMORROW = "tomorrow", + YESTERDAY = "yesterday", + ONE_WEEK_AGO = "one-week-ago", + ONE_WEEK_FROM_NOW = "one-week-from-now", + ONE_MONTH_AGO = "one-month-ago", + ONE_MONTH_FROM_NOW = "one-month-from-now", +} + +type FilterCondition = + | TextFilterCondition + | DateFilterCondition + | NumberFilterCondition; + +interface BaseFilter { + id: string; + columnId: string; + operator: FilterOperator; + isEnabled: boolean; +} + +/* Text filter */ +type TextCondition = TextFilterCondition; + +interface TextFilter extends BaseFilter { + type: CellType.TEXT; + condition: TextCondition; + text: string; +} + +/* File filter */ +type FileCondition = TextFilterCondition; + +interface FileFilter extends BaseFilter { + type: CellType.FILE; + condition: FileCondition; + text: string; +} + +/* Checkbox filter */ +type CheckboxCondition = + | TextFilterCondition.IS + | TextFilterCondition.IS_NOT; + +interface CheckboxFilter extends BaseFilter { + type: CellType.CHECKBOX; + condition: CheckboxCondition; + value: boolean; +} + +/* Tag filter */ +type TagCondition = + | TextFilterCondition.IS + | TextFilterCondition.IS_NOT + | TextFilterCondition.IS_EMPTY + | TextFilterCondition.IS_NOT_EMPTY; + +interface TagFilter extends BaseFilter { + type: CellType.TAG; + condition: TagCondition; + tagId: string; +} + +/* Multi-tag filter */ +type MultiTagCondition = + | TextFilterCondition.CONTAINS + | TextFilterCondition.DOES_NOT_CONTAIN + | TextFilterCondition.IS_EMPTY + | TextFilterCondition.IS_NOT_EMPTY; + +interface MultiTagFilter extends BaseFilter { + type: CellType.MULTI_TAG; + condition: MultiTagCondition; + tagIds: string[]; +} + +/* Embed filter */ +type EmbedCondition = + | TextFilterCondition.IS_EMPTY + | TextFilterCondition.IS_NOT_EMPTY; + +interface EmbedFilter extends BaseFilter { + type: CellType.EMBED; + condition: EmbedCondition; + text: string; +} + +/* Number filter */ +type NumberCondition = NumberFilterCondition; + +interface NumberFilter extends BaseFilter { + type: CellType.NUMBER; + condition: NumberCondition; + text: string; +} + +/* Date filter */ +type DateCondition = DateFilterCondition; + +interface DateFilter extends BaseFilter { + type: CellType.DATE; + condition: DateCondition; + option: DateFilterOption; + dateTime: string | null; +} + +/* Creation time filter */ +type CreationTimeCondition = + | DateFilterCondition.IS + | DateFilterCondition.IS_AFTER + | DateFilterCondition.IS_BEFORE; + +interface CreationTimeFilter extends BaseFilter { + type: CellType.CREATION_TIME; + condition: CreationTimeCondition; + option: DateFilterOption; + dateTime: string | null; +} + +/* Last edited filter */ +type LastEditedTimeCondition = + | DateFilterCondition.IS + | DateFilterCondition.IS_AFTER + | DateFilterCondition.IS_BEFORE; + +interface LastEditedTimeFilter extends BaseFilter { + type: CellType.LAST_EDITED_TIME; + condition: LastEditedTimeCondition; + option: DateFilterOption; + dateTime: string | null; +} + +/* Source File condition */ +type SourceFileCondition = TextFilterCondition; + +interface SourceFileFilter extends BaseFilter { + type: CellType.SOURCE_FILE; + condition: SourceFileCondition; + text: string; +} + +type Filter = + | TextFilter + | TagFilter + | MultiTagFilter + | CheckboxFilter + | FileFilter + | EmbedFilter + | NumberFilter + | DateFilter + | CreationTimeFilter + | LastEditedTimeFilter + | SourceFileFilter; + +interface BaseSource { + id: string; + type: SourceType; +} + +interface ObsidianFolderSource extends BaseSource { + type: SourceType.FOLDER; + path: string; + includeSubfolders: boolean; +} + +interface ObsidianFrontmatterSource extends BaseSource { + type: SourceType.FRONTMATTER; + propertyType: ObsidianPropertyType; + propertyKey: string; + filterCondition: + | TextFilterCondition + | NumberFilterCondition + | DateFilterCondition + | null; + filterText: string; +} + +type Source = ObsidianFolderSource | ObsidianFrontmatterSource; + +enum SourceType { + FOLDER = "folder", + FRONTMATTER = "frontmatter", +} diff --git a/src/shared/loom-state/types/loom-state.ts b/src/shared/loom-state/types/loom-state.ts index 9f70dcf2..c24cc3ba 100644 --- a/src/shared/loom-state/types/loom-state.ts +++ b/src/shared/loom-state/types/loom-state.ts @@ -1,9 +1,9 @@ -/******* Type definitions for v8.15.6 *******/ +/******* Type definitions for v8.15.13 *******/ import { ObsidianPropertyType } from "src/shared/frontmatter/types"; /** - * v8.15.6 + * v8.15.13 */ export interface LoomState { pluginVersion: string; @@ -53,6 +53,7 @@ export interface Column { horizontalPadding: PaddingSize; verticalPadding: PaddingSize; frontmatterKey: string | null; + multiTagSortDir: SortDir; } export interface Row { @@ -106,6 +107,7 @@ export interface TagCell extends BaseCell { export interface MultiTagCell extends BaseCell { tagIds: string[]; + multiTagSortDir: SortDir; } export interface NumberCell extends BaseCell { diff --git a/src/shared/loom-state/validate-state.ts b/src/shared/loom-state/validate-state.ts index 827bde1b..bb915ce1 100644 --- a/src/shared/loom-state/validate-state.ts +++ b/src/shared/loom-state/validate-state.ts @@ -344,6 +344,7 @@ const Column = Record({ aspectRatio: AspectRatioUnion, horizontalPadding: PaddingSizeUnion, verticalPadding: PaddingSizeUnion, + multiTagSortDir: SortDirUnion }); const BaseCell = Record({ diff --git a/versions.json b/versions.json index e6bf49bd..7f21a3b9 100644 --- a/versions.json +++ b/versions.json @@ -161,5 +161,6 @@ "8.15.9": "1.4.0", "8.15.10": "1.4.0", "8.15.11": "1.4.0", - "8.15.12": "1.4.0" + "8.15.12": "1.4.0", + "8.15.13": "1.4.0" } From c4d76200ebb1307d56dc60a2e60649196ecd209b Mon Sep 17 00:00:00 2001 From: Trey Wallis <40307803+trey-wallis@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:39:11 -0600 Subject: [PATCH 2/5] feat: add display name for sort direction --- src/react/loom-app/header-menu/option-submenu.tsx | 5 +++-- src/shared/loom-state/type-display-names.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/react/loom-app/header-menu/option-submenu.tsx b/src/react/loom-app/header-menu/option-submenu.tsx index 337849a4..057aaaec 100644 --- a/src/react/loom-app/header-menu/option-submenu.tsx +++ b/src/react/loom-app/header-menu/option-submenu.tsx @@ -8,7 +8,7 @@ import { DateFormatSeparator, NumberFormat, PaddingSize, - SortDir + SortDir, } from "src/shared/loom-state/types/loom-state"; import Stack from "src/react/shared/stack"; import Padding from "src/react/shared/padding"; @@ -18,6 +18,7 @@ import { getDisplayNameForCurrencyType, getDisplayNameForDateFormat, getDisplayNameForDateFormatSeparator, + getDisplayNameForSortDir, } from "src/shared/loom-state/type-display-names"; interface Props { @@ -171,7 +172,7 @@ export default function OptionSubmenu({ {type === CellType.MULTI_TAG && ( onSubmenuChange(SubmenuType.CONTENTS_SORT_DIR) } diff --git a/src/shared/loom-state/type-display-names.ts b/src/shared/loom-state/type-display-names.ts index 0c87aebf..6ff6277e 100644 --- a/src/shared/loom-state/type-display-names.ts +++ b/src/shared/loom-state/type-display-names.ts @@ -13,6 +13,7 @@ import { DateFilterOption, SourceType, DateFormatSeparator, + SortDir, } from "./types/loom-state"; const getShortDisplayNameForCalculation = (value: GeneralCalculation) => { @@ -169,6 +170,17 @@ export const getDisplayNameForDateFormatSeparator = ( } }; +export const getDisplayNameForSortDir = (dir: SortDir) => { + switch (dir) { + case SortDir.ASC: + return "Ascending"; + case SortDir.DESC: + return "Descending"; + default: + return "Default"; + } +} + export const getDisplayNameForCurrencyType = (type: CurrencyType) => { switch (type) { case CurrencyType.UNITED_STATES: From 4aafcc2a74a9d0d1fb47bd29cf85aadb3e27b576 Mon Sep 17 00:00:00 2001 From: Trey Wallis <40307803+trey-wallis@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:41:39 -0600 Subject: [PATCH 3/5] fix: don't close menu on sort select --- src/react/loom-app/header-menu/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/react/loom-app/header-menu/index.tsx b/src/react/loom-app/header-menu/index.tsx index abd80008..752987f2 100644 --- a/src/react/loom-app/header-menu/index.tsx +++ b/src/react/loom-app/header-menu/index.tsx @@ -89,7 +89,7 @@ export default function HeaderMenu({ numberSeparator, numberSuffix, frontmatterKey, - multiTagSortDir + multiTagSortDir, } = column; const [submenu, setSubmenu] = React.useState(null); const [localValue, setLocalValue] = React.useState(content); @@ -245,8 +245,6 @@ export default function HeaderMenu({ function handleMultiTagSortDirClick(value: SortDir) { onColumnChange(columnId, { multiTagSortDir: value }); - saveLocalValue(); - onClose(); setSubmenu(SubmenuType.OPTIONS); } From 499f0a0d111ed62c934ff59b8ca2bd6157ad354f Mon Sep 17 00:00:00 2001 From: Trey Wallis <40307803+trey-wallis@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:42:52 -0600 Subject: [PATCH 4/5] build: fix build errors --- .../loom-state/migrate/migrate-state-21.ts | 4 ++-- .../loom-state/migrate/migrate-state-22.ts | 12 +++++----- src/shared/loom-state/types/loom-state-21.ts | 24 ++++--------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/shared/loom-state/migrate/migrate-state-21.ts b/src/shared/loom-state/migrate/migrate-state-21.ts index 488d3b00..aa6c5da2 100644 --- a/src/shared/loom-state/migrate/migrate-state-21.ts +++ b/src/shared/loom-state/migrate/migrate-state-21.ts @@ -1,12 +1,12 @@ import MigrateState from "./migrate-state"; -import { LoomState20, LoomState } from "../types"; +import { LoomState20, LoomState21 } from "../types"; import { Row } from "../types/loom-state"; /** * Migrates to 8.15.6 */ export default class MigrateState21 implements MigrateState { - public migrate(prevState: LoomState20): LoomState { + public migrate(prevState: LoomState20): LoomState21 { const { rows } = prevState.model; const nextRows: Row[] = rows.map((row) => { diff --git a/src/shared/loom-state/migrate/migrate-state-22.ts b/src/shared/loom-state/migrate/migrate-state-22.ts index 9030d293..77e89671 100644 --- a/src/shared/loom-state/migrate/migrate-state-22.ts +++ b/src/shared/loom-state/migrate/migrate-state-22.ts @@ -1,6 +1,6 @@ import MigrateState from "./migrate-state"; -import { LoomState20, LoomState } from "../types"; -import { Row, SortDir, Column } from "../types/loom-state"; +import { LoomState } from "../types"; +import { SortDir, Column } from "../types/loom-state"; import { LoomState21 } from "../types/loom-state-21"; /** @@ -11,10 +11,10 @@ export default class MigrateState22 implements MigrateState { const { columns } = prevState.model; const nextColumns: Column[] = columns.map((column) => { - return { - ...column, - multiTagSortDir: SortDir.NONE, - }; + return { + ...column, + multiTagSortDir: SortDir.NONE, + }; }); return { diff --git a/src/shared/loom-state/types/loom-state-21.ts b/src/shared/loom-state/types/loom-state-21.ts index 42638375..7ad26806 100644 --- a/src/shared/loom-state/types/loom-state-21.ts +++ b/src/shared/loom-state/types/loom-state-21.ts @@ -80,17 +80,6 @@ interface EmbedCell extends BaseCell { alias: string | null; } -interface EmbedUrl { - type: "url"; - url: string; -} - -interface EmbedPath { - type: "path"; - path: string; - alias: string | null; -} - interface FileCell extends BaseCell { path: string; alias: string | null; //We save an alias to be able to serialize into the frontmatter @@ -309,11 +298,6 @@ enum DateFilterOption { ONE_MONTH_FROM_NOW = "one-month-from-now", } -type FilterCondition = - | TextFilterCondition - | DateFilterCondition - | NumberFilterCondition; - interface BaseFilter { id: string; columnId: string; @@ -470,10 +454,10 @@ interface ObsidianFrontmatterSource extends BaseSource { propertyType: ObsidianPropertyType; propertyKey: string; filterCondition: - | TextFilterCondition - | NumberFilterCondition - | DateFilterCondition - | null; + | TextFilterCondition + | NumberFilterCondition + | DateFilterCondition + | null; filterText: string; } From c32320ffb4f538da87db9fb61ab76543c70b11bf Mon Sep 17 00:00:00 2001 From: Trey Wallis <40307803+trey-wallis@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:44:02 -0600 Subject: [PATCH 5/5] chore: bump to 8.16.0 --- manifest.json | 2 +- package.json | 2 +- src/data/serialize-state.ts | 6 +++--- src/shared/loom-state/migrate/migrate-state-22.ts | 2 +- src/shared/loom-state/types/loom-state.ts | 12 ++++++------ versions.json | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/manifest.json b/manifest.json index 057f8d81..07261b2c 100644 --- a/manifest.json +++ b/manifest.json @@ -9,5 +9,5 @@ "fundingUrl": { "Buymeacoffee": "https://www.buymeacoffee.com/treywallis" }, - "version": "8.15.13" + "version": "8.16.0" } diff --git a/package.json b/package.json index a831cea3..d555ebbe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-dataloom", - "version": "8.15.13", + "version": "8.16.0", "description": "Weave together data from diverse sources into different views. Inspired by Excel Spreadsheets and Notion.so.", "main": "main.js", "scripts": { diff --git a/src/data/serialize-state.ts b/src/data/serialize-state.ts index 26b1a896..4524dd01 100644 --- a/src/data/serialize-state.ts +++ b/src/data/serialize-state.ts @@ -318,9 +318,9 @@ export const deserializeState = ( failedMigration = null; } - const VERSION_8_15_13 = "8.15.13"; - if (isVersionLessThan(fileVersion, VERSION_8_15_13)) { - failedMigration = VERSION_8_15_13; + const VERSION_8_16_0 = "8.16.0"; + if (isVersionLessThan(fileVersion, VERSION_8_16_0)) { + failedMigration = VERSION_8_16_0; const nextState = new MigrateState22().migrate( currentState as LoomState21 ); diff --git a/src/shared/loom-state/migrate/migrate-state-22.ts b/src/shared/loom-state/migrate/migrate-state-22.ts index 77e89671..e1a86281 100644 --- a/src/shared/loom-state/migrate/migrate-state-22.ts +++ b/src/shared/loom-state/migrate/migrate-state-22.ts @@ -4,7 +4,7 @@ import { SortDir, Column } from "../types/loom-state"; import { LoomState21 } from "../types/loom-state-21"; /** - * Migrates to 8.15.13 + * Migrates to 8.16.0 */ export default class MigrateState22 implements MigrateState { public migrate(prevState: LoomState21): LoomState { diff --git a/src/shared/loom-state/types/loom-state.ts b/src/shared/loom-state/types/loom-state.ts index c24cc3ba..09aa91c3 100644 --- a/src/shared/loom-state/types/loom-state.ts +++ b/src/shared/loom-state/types/loom-state.ts @@ -1,9 +1,9 @@ -/******* Type definitions for v8.15.13 *******/ +/******* Type definitions for v8.16.0 *******/ import { ObsidianPropertyType } from "src/shared/frontmatter/types"; /** - * v8.15.13 + * v8.16.0 */ export interface LoomState { pluginVersion: string; @@ -472,10 +472,10 @@ export interface ObsidianFrontmatterSource extends BaseSource { propertyType: ObsidianPropertyType; propertyKey: string; filterCondition: - | TextFilterCondition - | NumberFilterCondition - | DateFilterCondition - | null; + | TextFilterCondition + | NumberFilterCondition + | DateFilterCondition + | null; filterText: string; } diff --git a/versions.json b/versions.json index 7f21a3b9..dca1a04b 100644 --- a/versions.json +++ b/versions.json @@ -162,5 +162,5 @@ "8.15.10": "1.4.0", "8.15.11": "1.4.0", "8.15.12": "1.4.0", - "8.15.13": "1.4.0" + "8.16.0": "1.4.0" }