diff --git a/.config/typedoc.json b/.config/typedoc.json new file mode 100644 index 00000000..8f448f47 --- /dev/null +++ b/.config/typedoc.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPoints": ["../lib/index.ts"], + "out": "../dist/doc", + "basePath": "../lib", + "plugin": [ + "typedoc-plugin-missing-exports" + ] +} diff --git a/REUSE.toml b/REUSE.toml index f098715c..764546cf 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -6,7 +6,7 @@ SPDX-PackageSupplier = "Nextcloud " SPDX-PackageDownloadLocation = "https://github.com/nextcloud-libraries/nextcloud-files" [[annotations]] -path = ["package-lock.json", "package.json", "tsconfig.json"] +path = ["package-lock.json", "package.json", "tsconfig.json", ".config/typedoc.json"] precedence = "aggregate" SPDX-FileCopyrightText = "2019-2024 Nextcloud GmbH and Nextcloud contributors" SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/lib/fileListFilters.ts b/lib/fileListFilters.ts new file mode 100644 index 00000000..8715531f --- /dev/null +++ b/lib/fileListFilters.ts @@ -0,0 +1,140 @@ +/*! + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { emit } from '@nextcloud/event-bus' +import { TypedEventTarget } from 'typescript-event-target' +import { INode } from './files/node' + +/** + * Active filters can provide one or more "chips" to show the currently active state. + * Must at least provide a text representing the filters state and a callback to unset that state (disable this filter). + */ +export interface IFileListFilterChip { + /** + * Text of the chip + */ + text: string + /** + * Optional icon to be used on the chip (inline SVG as string) + */ + icon?: string + /** + * Handler to be called on click + */ + onclick: () => void +} + +/** + * This event is emitted when the the filter value changed and the file list needs to be updated + */ +export interface FilterUpdateEvent extends CustomEvent { + type: 'update:filter' +} + +/** + * This event is emitted when the the filter value changed and the file list needs to be updated + */ +export interface FilterUpdateChipsEvent extends CustomEvent { + type: 'update:chips' +} + +interface IFileListFilterEvents { + [name: string]: CustomEvent, + 'update:filter': FilterUpdateEvent + 'update:chips' : FilterUpdateChipsEvent +} + +export interface IFileListFilter extends TypedEventTarget { + + /** + * Unique ID of this filter + */ + readonly id: string + + /** + * Order of the filter + * + * Use a low number to make this filter ordered in front. + */ + readonly order: number + + /** + * If the filter needs a visual element for settings it can provide a function to mount it. + */ + readonly mount?: (el: HTMLElement) => void + + /** + * Filter function to decide if a node is shown + * @return The nodes to be shown + */ + filter(node: INode[]): INode[] +} + +export class FileListFilter extends TypedEventTarget implements IFileListFilter { + + public id: string + + public order: number + + constructor(id: string, order: number = 100) { + super() + this.id = id + this.order = order + } + + public filter(nodes: INode[]): INode[] { + throw new Error('Not implemented') + return nodes + } + + protected updateChips(chips: IFileListFilterChip[]) { + this.dispatchTypedEvent('update:chips', new CustomEvent('update:chips', { detail: chips }) as FilterUpdateChipsEvent) + } + + protected filterUpdated() { + this.dispatchTypedEvent('update:filter', new CustomEvent('update:filter') as FilterUpdateEvent) + } + +} + +/** + * Register a new filter on the file list + * + * This only must be called once to register the filter, + * when the filter state changes you need to call `filterUpdated` on the filter instead. + * + * @param filter The filter to register on the file list + */ +export function registerFileListFilter(filter: IFileListFilter): void { + if (!window._nc_filelist_filters) { + window._nc_filelist_filters = new Map() + } + if (window._nc_filelist_filters.has(filter.id)) { + throw new Error(`File list filter "${filter.id}" already registered`) + } + window._nc_filelist_filters.set(filter.id, filter) + emit('files:filter:added', filter) +} + +/** + * Remove a registered filter from the file list + * @param filterId The unique ID of the filter to remove + */ +export function unregisterFileListFilter(filterId: string): void { + if (window._nc_filelist_filters && window._nc_filelist_filters.has(filterId)) { + window._nc_filelist_filters.delete(filterId) + emit('files:filter:removed', filterId) + } +} + +/** + * Get all registered file list filters + */ +export function getFileListFilters(): IFileListFilter[] { + if (!window._nc_filelist_filters) { + return [] + } + return [...window._nc_filelist_filters.values()] +} diff --git a/lib/index.ts b/lib/index.ts index fc1d6f5c..dcf95135 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -26,9 +26,8 @@ export { formatFileSize, parseFileSize } from './utils/fileSize' export { orderBy, type SortingOrder } from './utils/sorting' export { sortNodes, FilesSortingMode, type FilesSortingOptions } from './utils/fileSorting' -export * from './navigation/navigation' -export * from './navigation/column' -export * from './navigation/view' +export * from './navigation/index' +export * from './fileListFilters' /** * Add a new menu entry to the upload manager menu diff --git a/lib/navigation/index.ts b/lib/navigation/index.ts new file mode 100644 index 00000000..d08cee54 --- /dev/null +++ b/lib/navigation/index.ts @@ -0,0 +1,8 @@ +/*! + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +export * from './navigation' +export * from './column' +export * from './view' diff --git a/package-lock.json b/package-lock.json index 1e29102f..205a15fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,14 @@ "@nextcloud/sharing": "^0.2.2", "cancelable-promise": "^4.3.1", "is-svg": "^5.0.1", + "typedoc-plugin-missing-exports": "^3.0.0", "typescript-event-target": "^1.1.1", "webdav": "^5.6.0" }, "devDependencies": { "@codecov/vite-plugin": "^0.0.1-beta.10", "@nextcloud/eslint-config": "^8.4.1", + "@nextcloud/event-bus": "^3.3.1", "@nextcloud/typings": "^1.9.1", "@nextcloud/vite-config": "^2.1.0", "@types/node": "^20.14.11", @@ -1309,36 +1311,22 @@ } }, "node_modules/@nextcloud/event-bus": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-3.2.0.tgz", - "integrity": "sha512-5F3Zjqv12r/VCkzXFnsMZb/lb12eVv7/oDSUFV3njNJoPdu7x6k5kjRM/N4THHoDDRRgBQjgKr9Uyqgppqj6XA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-3.3.1.tgz", + "integrity": "sha512-VBYJspOVk5aZopgZwCUoMKFqcTLCNel2TLvtu0HMPV2gR5ZLPiPAKbkyKkYTh+Sd5QB1gR6l3STTv1gyal0soQ==", "dependencies": { - "@types/node": "^20.12.7", - "semver": "^7.6.0" + "@types/node": "^20.12.12", + "semver": "^7.6.2" }, "engines": { "node": "^20.0.0", "npm": "^10.0.0" } }, - "node_modules/@nextcloud/event-bus/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@nextcloud/event-bus/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -1346,11 +1334,6 @@ "node": ">=10" } }, - "node_modules/@nextcloud/event-bus/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/@nextcloud/initial-state": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@nextcloud/initial-state/-/initial-state-2.2.0.tgz", @@ -1953,8 +1936,7 @@ "node_modules/@shikijs/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.0.tgz", - "integrity": "sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg==", - "dev": true + "integrity": "sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg==" }, "node_modules/@types/argparse": { "version": "1.0.38", @@ -2661,8 +2643,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", @@ -6229,7 +6210,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, "dependencies": { "uc.micro": "^2.0.0" } @@ -6294,8 +6274,7 @@ "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" }, "node_modules/magic-string": { "version": "0.30.10", @@ -6348,7 +6327,6 @@ "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -6385,8 +6363,7 @@ "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" }, "node_modules/meow": { "version": "13.1.0", @@ -7196,7 +7173,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, "engines": { "node": ">=6" } @@ -7704,7 +7680,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.0.tgz", "integrity": "sha512-YD2sXQ+TMD/F9BimV9Jn0wj35pqOvywvOG/3PB6hGHyGKlM7TJ9tyJ02jOb2kF8F0HfJwKNYrh3sW7jEcuRlXA==", - "dev": true, "dependencies": { "@shikijs/core": "1.10.0" } @@ -8415,7 +8390,6 @@ "version": "0.26.4", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.4.tgz", "integrity": "sha512-FlW6HpvULDKgc3rK04V+nbFyXogPV88hurarDPOjuuB5HAwuAlrCMQ5NeH7Zt68a/ikOKu6Z/0hFXAeC9xPccQ==", - "dev": true, "dependencies": { "lunr": "^2.3.9", "markdown-it": "^14.1.0", @@ -8433,11 +8407,18 @@ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x" } }, + "node_modules/typedoc-plugin-missing-exports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-3.0.0.tgz", + "integrity": "sha512-R7D8fYrK34mBFZSlF1EqJxfqiUSlQSmyrCiQgTQD52nNm6+kUtqwiaqaNkuJ2rA2wBgWFecUA8JzHT7x2r7ePg==", + "peerDependencies": { + "typedoc": "0.26.x" + } + }, "node_modules/typedoc/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -8446,7 +8427,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8461,7 +8441,6 @@ "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8478,8 +8457,7 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/unbox-primitive": { "version": "1.0.2", @@ -9721,7 +9699,6 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index 84c9aae0..9a8655ee 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ ], "scripts": { "build": "vite --mode production build", - "build:doc": "typedoc --out dist/doc lib && touch dist/doc/.nojekyll", + "build:doc": "typedoc && touch dist/doc/.nojekyll", "dev": "vite --mode development build", "watch": "vite --mode development build --watch", "lint": "eslint .", @@ -53,6 +53,7 @@ "devDependencies": { "@codecov/vite-plugin": "^0.0.1-beta.10", "@nextcloud/eslint-config": "^8.4.1", + "@nextcloud/event-bus": "^3.3.1", "@nextcloud/typings": "^1.9.1", "@nextcloud/vite-config": "^2.1.0", "@types/node": "^20.14.11", @@ -75,6 +76,7 @@ "@nextcloud/sharing": "^0.2.2", "cancelable-promise": "^4.3.1", "is-svg": "^5.0.1", + "typedoc-plugin-missing-exports": "^3.0.0", "typescript-event-target": "^1.1.1", "webdav": "^5.6.0" }