Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/assets/CustomFields-Selection-Filter.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 83 additions & 0 deletions docs/features/user-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,89 @@ Each user field has the following properties:
- **Date**: A date.
- **List**: A list of values.

## File Suggestion Filtering (Advanced)

When using text or list type custom fields, you can configure **autosuggestion filters** to control which files appear in the autocomplete dropdown when you type `[[` in the field.

![Custom Field Filtering](../assets/CustomFields-Selection-Filter.gif)

This is useful when you want to limit suggestions to specific types of notes. For example:
- An "Assignee" field that only suggests notes tagged with `#person`
- A "Project" field that only shows notes in the `Projects/` folder
- A "Related Document" field filtered by a specific frontmatter property

### Configuring Filters

To configure filters for a custom field:

1. Go to **Settings → Task Properties → Custom User Fields**
2. Expand the custom field card you want to configure
3. Expand the **"Autosuggestion filters (Advanced)"** section
4. Configure one or more of the following filters:

#### Filter Options

- **Required tags**: Only show files that have ANY of these tags (comma-separated)
- Example: `person, team` - shows files with either `#person` OR `#team` tag
- Supports hierarchical tags: `project/active` matches `#project/active`

- **Include folders**: Only show files in these folders (comma-separated)
- Example: `People/, Teams/` - shows files in either folder
- Supports nested folders: `Projects/Active/` matches files in that specific folder

- **Required property key**: Only show files that have this frontmatter property
- Example: `role` - shows files with a `role:` property in frontmatter

- **Required property value**: Expected value for the property (optional)
- Example: `developer` - when combined with property key `role`, shows files with `role: developer`
- Leave empty to match any value (just checks property exists)

#### Filter Indicator

When filters are configured, a **"Filters On"** badge with a funnel icon appears next to the section title. This prevents you from forgetting that filters are active.

### Filter Behavior

- **All filters are combined with AND logic**: Files must match ALL configured filters to appear
- **Empty filters are ignored**: If you don't configure a filter, it won't restrict results
- **No filters = all files**: If no filters are configured, all markdown files in your vault will appear
- **Filters only affect autocomplete**: They don't affect the actual field value or validation

### Example Configurations

#### Assignee Field (People Only)
```
Display Name: Assignee
Property Key: assignee
Type: List

Autosuggestion filters:
Required tags: person
Include folders: People/
```

#### Project Field (Active Projects)
```
Display Name: Project
Property Key: project
Type: Text

Autosuggestion filters:
Required tags: project
Required property key: status
Required property value: active
```

#### Related Note (Specific Folder)
```
Display Name: Related Note
Property Key: related-note
Type: Text

Autosuggestion filters:
Include folders: Documentation/, Guides/
```

## Using User Fields

Once you have created a user field, it will be available in the following places:
Expand Down
23 changes: 22 additions & 1 deletion docs/settings/task-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,25 @@ Configure which frontmatter properties TaskNotes should use for each field. Task

## Custom User Fields

Define custom frontmatter properties to appear as type-aware filter options across views. Each row has a Display Name, Property Name, and Type.
Define custom frontmatter properties to appear as type-aware filter options across views. Each field has:

- **Display Name**: How the field appears in the UI
- **Property Key**: The frontmatter property name
- **Type**: Data type (text, number, boolean, date, or list)

### Autosuggestion Filters (Advanced)

Each custom field can optionally configure **autosuggestion filters** to control which files appear when using the `[[` wikilink autocomplete in that field.

![Custom Field Filtering](../assets/CustomFields-Selection-Filter.gif)

**Filter Options:**
- **Required tags**: Comma-separated list of tags (shows files with ANY of these tags)
- **Include folders**: Comma-separated list of folder paths (shows files in ANY of these folders)
- **Required property key**: Frontmatter property that must exist
- **Required property value**: Expected value for the property (optional)

**Visual Indicator:**
When filters are configured, a **"Filters On"** badge appears to remind you that suggestions are being filtered.

**See Also:** [User Fields Feature Documentation](../features/user-fields.md#file-suggestion-filtering-advanced) for detailed examples and configuration guide.
2 changes: 2 additions & 0 deletions i18n.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@
"settings.taskProperties.customUserFields.defaultNames.unnamedField": "79e874c0dd8787bce649a46a41a600637a38b998",
"settings.taskProperties.customUserFields.defaultNames.noKey": "576426d52f216a70835ec62c0172ee7f762f8199",
"settings.taskProperties.customUserFields.deleteTooltip": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"settings.taskProperties.customUserFields.autosuggestFilters.header": "54bbd5ce9872e3040735f334d7a52a22dfd80868",
"settings.taskProperties.customUserFields.autosuggestFilters.description": "61290f286c6963dbe266b1b6b20a4c3f29c78d66",
"settings.appearance.taskCards.header": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"settings.appearance.taskCards.description": "4554f6f0816fa0c30fb2d11d11b7a4e0e9a610de",
"settings.appearance.taskCards.defaultVisibleProperties.name": "0aa5efd8c0508868b5a726a4f3890fc3efb71d26",
Expand Down
12 changes: 12 additions & 0 deletions i18n.state.json
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "1323694046557ec28a86f1d61b62ea5196d40ddd"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "20a69cc45e76fad40738bab186f0ffa7be6f8d99"
Expand Down Expand Up @@ -8122,6 +8124,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "7b7f39c5646078ab8350766132d94cd51d7ea8f6"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "06c1127504474b20e7e52386ba6905005993e026"
Expand Down Expand Up @@ -14088,6 +14092,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "c965afce3ec1984899fb69c1068a2f6168271011"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "642148984b7b1f1908934199cb4f3fb2e857bd01"
Expand Down Expand Up @@ -19988,6 +19994,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "c693364ea2d0cda28689bd4c11f91a5caabd7579"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "8eecc3ac22ee85969f71f2b2cecd98975959028c"
Expand Down Expand Up @@ -25789,6 +25797,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "eba675fad82b57bcf437709ef3391f8239b85215"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "529b26b313f5f06e0757d38c9f04b72c5150d4bc"
Expand Down Expand Up @@ -31674,6 +31684,8 @@
"source": "5ac477c267332c84ac6f5fe5509d523e08a4af94",
"translation": "b9cee430291309e3229482a2d63d9e58a02642df"
},
"settings.taskProperties.customUserFields.autosuggestFilters.header": null,
"settings.taskProperties.customUserFields.autosuggestFilters.description": null,
"settings.appearance.taskCards.header": {
"source": "08836d307ea5b1614f69962e1b9d36f243baddc5",
"translation": "c15beaf879843058d9b229f03f12c62f0150178e"
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/resources/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,11 @@ export const en: TranslationTree = {
noKey: "no-key",
},
deleteTooltip: "Delete field",
autosuggestFilters: {
header: "Autosuggestion filters (Advanced)",
description:
"Filter which files appear in autocomplete suggestions for this field",
},
},
},
appearance: {
Expand Down
9 changes: 7 additions & 2 deletions src/modals/TaskModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1768,8 +1768,13 @@ class UserFieldSuggest extends AbstractInputSuggest<UserFieldSuggestion> {
if (wikiMatch) {
const partial = wikiMatch[1] || "";
const { FileSuggestHelper } = await import("../suggest/FileSuggestHelper");
// Custom fields show ALL files (no filterConfig = no filtering)
const list = await FileSuggestHelper.suggest(this.plugin, partial);
// Apply custom field filter if configured, otherwise show all files
const list = await FileSuggestHelper.suggest(
this.plugin,
partial,
20,
this.fieldConfig.autosuggestFilter
);
return list.map((item) => ({
value: item.insertText,
display: item.displayText,
Expand Down
99 changes: 99 additions & 0 deletions src/settings/components/FilterSettingsComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { TranslationKey } from "../../i18n";
import type { FileFilterConfig } from "../../suggest/FileSuggestHelper";
import { createCardInput } from "./CardComponent";

/**
* Creates filter settings inputs (tags, folders, property key/value)
* Reusable across project autosuggest and custom field filters
*/
export function createFilterSettingsInputs(
container: HTMLElement,
currentConfig: FileFilterConfig | undefined,
onChange: (updated: FileFilterConfig) => void,
translate: (key: TranslationKey) => string
): void {
// Track current config state to preserve values across sequential updates
let config = currentConfig || {
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};

// Helper to update config and trigger onChange
const updateConfig = (updates: Partial<FileFilterConfig>) => {
config = { ...config, ...updates };
onChange(config);
};

// Required Tags input
const tagsInput = createCardInput(
"text",
translate("settings.appearance.projectAutosuggest.requiredTags.placeholder"),
config.requiredTags?.join(", ") || ""
);
tagsInput.addEventListener("change", () => {
const tags = tagsInput.value
.split(",")
.map((t) => t.trim())
.filter(Boolean);
updateConfig({ requiredTags: tags });
});

// Include Folders input
const foldersInput = createCardInput(
"text",
translate("settings.appearance.projectAutosuggest.includeFolders.placeholder"),
config.includeFolders?.join(", ") || ""
);
foldersInput.addEventListener("change", () => {
const folders = foldersInput.value
.split(",")
.map((f) => f.trim())
.filter(Boolean);
updateConfig({ includeFolders: folders });
});

// Property Key input
const keyInput = createCardInput(
"text",
translate("settings.appearance.projectAutosuggest.requiredPropertyKey.placeholder"),
config.propertyKey || ""
);
keyInput.addEventListener("change", () => {
updateConfig({ propertyKey: keyInput.value.trim() });
});

// Property Value input
const valueInput = createCardInput(
"text",
translate("settings.appearance.projectAutosuggest.requiredPropertyValue.placeholder"),
config.propertyValue || ""
);
valueInput.addEventListener("change", () => {
updateConfig({ propertyValue: valueInput.value.trim() });
});

// Create rows
const createRow = (label: string, input: HTMLElement) => {
const row = container.createDiv("tasknotes-settings__card-config-row");
const labelEl = row.createSpan("tasknotes-settings__card-config-label");
labelEl.textContent = label;
row.appendChild(input);
};

createRow(translate("settings.appearance.projectAutosuggest.requiredTags.name"), tagsInput);
createRow(
translate("settings.appearance.projectAutosuggest.includeFolders.name"),
foldersInput
);
createRow(
translate("settings.appearance.projectAutosuggest.requiredPropertyKey.name"),
keyInput
);
createRow(
translate("settings.appearance.projectAutosuggest.requiredPropertyValue.name"),
valueInput
);
}

Loading