// // Copyright © 2020, 2021 Anticrm Platform Contributors. // Copyright © 2021 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import type { AnyAttribute, Class, Client, Doc, DocumentQuery, FindOptions, Lookup, Mixin, Obj, ObjQueryType, Ref, SortingOrder, Space, Type, UXObject } from '@hcengineering/core' import { Asset, IntlString, Plugin, plugin, Resource, Status } from '@hcengineering/platform' import type { Preference } from '@hcengineering/preference' import type { AnyComponent, AnySvelteComponent, PopupAlignment, PopupPosAlignment, Location as PlatformLocation } from '@hcengineering/ui' /** * @public */ export interface KeyFilter { _class: Ref> key: string component: AnyComponent label: IntlString icon: Asset | AnySvelteComponent | undefined } /** * @public */ export interface FilterMode extends Doc { label: IntlString disableValueSelector?: boolean result: Resource<(filter: Filter, onUpdate: () => void) => Promise>> } /** * @public */ export interface Filter { key: KeyFilter nested?: Filter mode: Ref modes: Ref[] value: any[] props?: Record index: number onRemove?: () => void } /** * @public */ export interface FilteredView extends Preference { name: string location: PlatformLocation filters: string viewOptions?: ViewOptions viewletId?: Ref | null } /** * @public */ export interface ClassFilters extends Class { filters: (KeyFilter | string)[] } /** * @public */ export interface AttributeFilter extends Class> { component: AnyComponent } /** * @public */ export interface AttributeEditor extends Class { inlineEditor: AnyComponent // If defined could be used for ShowEditor declarative actions. popup?: AnyComponent } /** * @public */ export interface CollectionEditor extends Class { editor: AnyComponent inlineEditor?: AnyComponent } /** * @public */ export interface InlineAttributEditor extends Class { editor: AnyComponent } /** * @public */ export interface ArrayEditor extends Class { editor?: AnyComponent inlineEditor?: AnyComponent } /** * @public */ export interface CollectionPresenter extends Class { presenter: AnyComponent } /** * @public */ export interface AttributePresenter extends Class { presenter: AnyComponent } /** * @public */ export interface ObjectPresenter extends Class { presenter: AnyComponent } /** * @public */ export interface ListItemPresenter extends Class { presenter: AnyComponent } /** * @public */ export interface ObjectEditor extends Class { editor: AnyComponent pinned?: boolean } /** * @public */ export interface SpaceHeader extends Class { header: AnyComponent } /** * @public */ export interface SpaceName extends Class { getName: Resource<(client: Client, space: Space) => Promise> } /** * @public */ export interface ObjectEditorHeader extends Class { editor: AnyComponent } /** * @public */ export interface ObjectValidator extends Class { validator: Resource<(doc: T, client: Client) => Promise> } /** * @public */ export interface ObjectTitle extends Class { titleProvider: Resource<(client: Client, ref: Ref) => Promise> } /** * @public */ export interface ViewletDescriptor extends Doc, UXObject { component: AnyComponent } /** * @public */ export interface ListHeaderExtra extends Class { presenters: AnyComponent[] } /** * @public */ export type SortFunc = Resource<(values: any[]) => Promise> /** * @public */ export interface ClassSortFuncs extends Class { func: SortFunc } /** * @public */ export interface Viewlet extends Doc { attachTo: Ref> descriptor: Ref options?: FindOptions config: (BuildModelKey | string)[] hiddenKeys?: string[] viewOptions?: ViewOptionsModel variant?: string } /** * @public */ export interface LinkPresenter extends Doc { pattern: string component: AnyComponent } /** * @public * * "Alt + K" =\> Alt and K should be pressed together * "J T" - J and then T shold be pressed. */ export type KeyBinding = string /** * @public */ export type ViewActionInput = 'focus' | 'selection' | 'any' | 'none' /** * @public */ export type ViewAction> = Resource< (doc: Doc | Doc[] | undefined, evt: Event, params?: T) => Promise > /** * @public */ export interface ActionCategory extends Doc, UXObject { // Does category is visible for use in popup. visible: boolean } /** * @public */ export type ActionGroup = 'create' | 'edit' | 'associate' | 'copy' | 'tools' | 'other' /** * @public */ export interface Action> extends Doc, UXObject { // Action implementation details action: ViewAction

// Action implementation parameters actionProps?: P // If specified, will show sub menu based on actionPopup/actionProps actionPopup?: AnyComponent // If specified, action could be used only with one item selected. // single - one object is required // any - one or multiple objects are required // any - any input is suitable. input: ViewActionInput inline?: boolean // Focus and/or all selection document should match target class. target: Ref> // Action is applicable only for objects matching criteria query?: DocumentQuery // If defined, types should be matched to proposed list inputProps?: Record>> // Kayboard bindings keyBinding?: KeyBinding[] // short description for action. description?: IntlString // Action category, for UI. category: Ref // Context action is defined for context: ViewContext // A list of actions replaced by this one. // For example it could be global action and action for focus class, second one fill override first one. override?: Ref[] // Avaible only for workspace owners secured?: boolean } /** * @public * context - only for context menu actions. * workbench - global actions per application or entire workbench. * browser - actions for list/table/kanban browsing. * editor - actions for selected editor context. * panel - for panel based actions. * popup - for popup based actions, like Close of Popup. * input - for input based actions, some actions should be available for input controls. */ export type ViewContextType = 'context' | 'workbench' | 'browser' | 'editor' | 'panel' | 'popup' | 'input' | 'none' /** * @public */ export interface ViewContext { mode: ViewContextType | ViewContextType[] // Active application application?: Ref // Optional groupping group?: ActionGroup } /** * @public */ export interface IgnoreActions extends Class { actions: Ref[] } /** * @public */ export interface PreviewPresenter extends Class { presenter: AnyComponent } /** * @public */ export const viewId = 'view' as Plugin /** * @public */ export interface BuildModelKey { key: string presenter?: AnyComponent | AnySvelteComponent // A set of extra props passed to presenter. props?: Record label?: IntlString sortingKey?: string | string[] // On client sorting function sortingFunction?: (a: Doc, b: Doc) => number } /** * @public */ export interface AttributeModel { key: string label: IntlString _class: Ref> presenter: AnySvelteComponent // Extra properties for component props?: Record sortingKey: string | string[] optional?: boolean excludeByKey?: string // Extra icon if applicable icon?: Asset attribute?: AnyAttribute collectionAttr: boolean castRequest?: Ref> } /** * @public */ export interface BuildModelOptions { client: Client _class: Ref> keys: (BuildModelKey | string)[] lookup?: Lookup ignoreMissing?: boolean } /** * Define document create popup widget * * @public * */ export interface ObjectFactory extends Class { component?: AnyComponent create?: Resource<(props?: Record) => Promise> } /** * @public */ export interface ViewletPreference extends Preference { attachedTo: Ref config: (BuildModelKey | string)[] } /** * @public */ export type ViewOptions = { groupBy: string orderBy: OrderOption } & Record /** * @public */ export interface ViewOption { type: string key: string defaultValue: any label: IntlString hidden?: (viewOptions: ViewOptions) => boolean actionTartget?: 'query' action?: Resource<(value: any, ...params: any) => any> } /** * @public */ export interface ViewQueryOption extends ViewOption { actionTartget: 'query' action: Resource<(value: any, query: DocumentQuery) => DocumentQuery> } /** * @public */ export interface ToggleViewOption extends ViewOption { type: 'toggle' defaultValue: boolean } /** * @public */ export interface DropdownViewOption extends ViewOption { type: 'dropdown' defaultValue: string values: Array<{ label: IntlString, id: string, hidden?: (viewOptions: ViewOptions) => boolean }> } /** * @public */ export type ViewOptionModel = ToggleViewOption | DropdownViewOption /** * @public */ export type OrderOption = [string, SortingOrder] /** * @public */ export interface ViewOptionsModel { groupBy: string[] orderBy: OrderOption[] other: ViewOptionModel[] } /** * @public */ const view = plugin(viewId, { mixin: { ClassFilters: '' as Ref>, AttributeFilter: '' as Ref>, AttributeEditor: '' as Ref>, CollectionPresenter: '' as Ref>, CollectionEditor: '' as Ref>, InlineAttributEditor: '' as Ref>, ArrayEditor: '' as Ref>, AttributePresenter: '' as Ref>, ListItemPresenter: '' as Ref>, ObjectEditor: '' as Ref>, ObjectPresenter: '' as Ref>, ObjectEditorHeader: '' as Ref>, ObjectValidator: '' as Ref>, ObjectFactory: '' as Ref>, ObjectTitle: '' as Ref>, SpaceHeader: '' as Ref>, SpaceName: '' as Ref>, IgnoreActions: '' as Ref>, PreviewPresenter: '' as Ref>, ListHeaderExtra: '' as Ref>, SortFuncs: '' as Ref> }, class: { ViewletPreference: '' as Ref>, ViewletDescriptor: '' as Ref>, Viewlet: '' as Ref>, Action: '' as Ref>, ActionCategory: '' as Ref>, LinkPresenter: '' as Ref>, FilterMode: '' as Ref>, FilteredView: '' as Ref> }, action: { Delete: '' as Ref, Move: '' as Ref, MoveLeft: '' as Ref, MoveRight: '' as Ref, MoveUp: '' as Ref, MoveDown: '' as Ref, SelectItem: '' as Ref, SelectItemAll: '' as Ref, SelectItemNone: '' as Ref, SelectUp: '' as Ref, SelectDown: '' as Ref, ShowPreview: '' as Ref, ShowActions: '' as Ref, // Edit document Open: '' as Ref }, viewlet: { Table: '' as Ref, List: '' as Ref }, component: { ObjectPresenter: '' as AnyComponent, EditDoc: '' as AnyComponent, SpacePresenter: '' as AnyComponent, BooleanTruePresenter: '' as AnyComponent, ValueSelector: '' as AnyComponent, GrowPresenter: '' as AnyComponent }, string: { CustomizeView: '' as IntlString, LabelNA: '' as IntlString, View: '' as IntlString, FilteredViews: '' as IntlString, NewFilteredView: '' as IntlString, FilteredViewName: '' as IntlString, List: '' as IntlString, Timeline: '' as IntlString }, icon: { Table: '' as Asset, List: '' as Asset, Card: '' as Asset, Timeline: '' as Asset, Delete: '' as Asset, MoreH: '' as Asset, Move: '' as Asset, Archive: '' as Asset, Statuses: '' as Asset, Setting: '' as Asset, Open: '' as Asset, ArrowRight: '' as Asset, Views: '' as Asset, Pin: '' as Asset, Model: '' as Asset, ViewButton: '' as Asset }, category: { General: '' as Ref, GeneralNavigation: '' as Ref, Navigation: '' as Ref, Editor: '' as Ref, MarkdownFormatting: '' as Ref }, filter: { FilterObjectIn: '' as Ref, FilterObjectNin: '' as Ref, FilterValueIn: '' as Ref, FilterValueNin: '' as Ref, FilterBefore: '' as Ref, FilterAfter: '' as Ref, FilterNestedMatch: '' as Ref, FilterNestedDontMatch: '' as Ref }, popup: { PositionElementAlignment: '' as Resource<(e?: Event) => PopupAlignment | undefined> }, actionImpl: { CopyTextToClipboard: '' as ViewAction<{ textProvider: Resource<(doc: Doc, props: Record) => Promise> props?: Record }>, UpdateDocument: '' as ViewAction<{ key: string value: any ask?: boolean label?: IntlString message?: IntlString }>, ShowPanel: '' as ViewAction<{ component?: AnyComponent element?: PopupPosAlignment rightSection?: AnyComponent }>, ShowPopup: '' as ViewAction<{ component: AnyComponent element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined> _id?: string _class?: string _space?: string value?: string values?: string props?: Record // Will copy values from selection document to props fillProps?: Record }>, ShowEditor: '' as ViewAction<{ element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined> attribute: string props?: Record }>, ValueSelector: '' as ViewAction<{ attribute: string // Class object finder _class?: Ref> query?: DocumentQuery queryOptions?: FindOptions // Will copy values from selection document to query // If set of docs passed, will do $in for values. fillQuery?: Record // A list of fields with matched values to perform action. docMatches?: string[] searchField?: string // Or list of values to select from values?: { icon?: Asset, label: IntlString, id: number | string }[] placeholder?: IntlString }> } }) export default view