From 35e4fcd8aa7df6182a9db03acc821c6a4e3d8780 Mon Sep 17 00:00:00 2001 From: Alexander Platov Date: Fri, 4 Nov 2022 09:07:27 +0300 Subject: [PATCH] Added hyperlink type, ScrollerBar (#2353) Signed-off-by: Alexander Platov --- models/core/src/core.ts | 4 + models/core/src/index.ts | 2 + models/setting/src/index.ts | 4 + models/setting/src/plugin.ts | 1 + models/view/src/index.ts | 11 + models/view/src/plugin.ts | 1 + packages/core/src/classes.ts | 10 + packages/core/src/component.ts | 5 +- packages/core/src/lang/en.json | 3 +- packages/core/src/lang/ru.json | 3 +- packages/model/src/dsl.ts | 8 + packages/theme/styles/_layouts.scss | 1 + packages/ui/src/components/ScrollerBar.svelte | 189 +++++++++++++++++ packages/ui/src/index.ts | 1 + .../typeEditors/HyperlinkTypeEditor.svelte | 25 +++ plugins/setting-resources/src/index.ts | 2 + .../src/components/state/StatesBar.svelte | 194 ++---------------- .../src/components/HyperlinkPresenter.svelte | 27 +++ plugins/view-resources/src/index.ts | 2 + 19 files changed, 311 insertions(+), 182 deletions(-) create mode 100644 packages/ui/src/components/ScrollerBar.svelte create mode 100644 plugins/setting-resources/src/components/typeEditors/HyperlinkTypeEditor.svelte create mode 100644 plugins/view-resources/src/components/HyperlinkPresenter.svelte diff --git a/models/core/src/core.ts b/models/core/src/core.ts index 4b70af4f19..237236cb48 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -143,6 +143,10 @@ export class TType extends TObj implements Type { @Model(core.class.TypeString, core.class.Type) export class TTypeString extends TType {} +@UX(core.string.Hyperlink) +@Model(core.class.TypeHyperlink, core.class.Type) +export class TTypeHyperlink extends TType {} + @UX(core.string.IntlString) @Model(core.class.TypeIntlString, core.class.Type) export class TTypeIntlString extends TType {} diff --git a/models/core/src/index.ts b/models/core/src/index.ts index ba87a80b23..2279016621 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -38,6 +38,7 @@ import { TTypeMarkup, TTypeNumber, TTypeString, + TTypeHyperlink, TTypeTimestamp, TTypeRelatedDocument, TVersion @@ -81,6 +82,7 @@ export function createModel (builder: Builder): void { TTypeNumber, TTypeBoolean, TTypeString, + TTypeHyperlink, TCollection, TVersion, TTypeIntlString, diff --git a/models/setting/src/index.ts b/models/setting/src/index.ts index bf26f8110a..5f46b183ba 100644 --- a/models/setting/src/index.ts +++ b/models/setting/src/index.ts @@ -278,6 +278,10 @@ export function createModel (builder: Builder): void { editor: setting.component.StringTypeEditor }) + builder.mixin(core.class.TypeHyperlink, core.class.Class, view.mixin.ObjectEditor, { + editor: setting.component.HyperlinkTypeEditor + }) + builder.mixin(core.class.TypeBoolean, core.class.Class, view.mixin.ObjectEditor, { editor: setting.component.BooleanTypeEditor }) diff --git a/models/setting/src/plugin.ts b/models/setting/src/plugin.ts index 5664b7ef5b..44945b2373 100644 --- a/models/setting/src/plugin.ts +++ b/models/setting/src/plugin.ts @@ -33,6 +33,7 @@ export default mergeIds(settingId, setting, { component: { EnumSetting: '' as AnyComponent, StringTypeEditor: '' as AnyComponent, + HyperlinkTypeEditor: '' as AnyComponent, BooleanTypeEditor: '' as AnyComponent, NumberTypeEditor: '' as AnyComponent, DateTypeEditor: '' as AnyComponent, diff --git a/models/view/src/index.ts b/models/view/src/index.ts index f2927c0312..816f17df04 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -295,6 +295,13 @@ export function createModel (builder: Builder): void { view.component.StringEditor, view.component.StringEditorPopup ) + classPresenter( + builder, + core.class.TypeHyperlink, + view.component.HyperlinkPresenter, + view.component.StringEditor, + view.component.StringEditorPopup + ) classPresenter(builder, core.class.TypeIntlString, view.component.IntlStringPresenter) classPresenter(builder, core.class.TypeNumber, view.component.NumberPresenter, view.component.NumberEditor) classPresenter( @@ -520,6 +527,10 @@ export function createModel (builder: Builder): void { component: view.component.ValueFilter }) + builder.mixin(core.class.TypeHyperlink, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) + builder.mixin(core.class.TypeBoolean, core.class.Class, view.mixin.AttributeFilter, { component: view.component.ValueFilter }) diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index 479993fe4a..ccc7dabb48 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -43,6 +43,7 @@ export default mergeIds(viewId, view, { StringEditor: '' as AnyComponent, StringEditorPopup: '' as AnyComponent, StringPresenter: '' as AnyComponent, + HyperlinkPresenter: '' as AnyComponent, IntlStringPresenter: '' as AnyComponent, NumberEditor: '' as AnyComponent, NumberPresenter: '' as AnyComponent, diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 1fd8644b95..6eba3d3582 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -36,6 +36,11 @@ export type Timestamp = number */ export type Markup = string +/** + * @public + */ +export type Hyperlink = string + /** * @public */ @@ -228,6 +233,11 @@ export interface EnumOf extends Type { of: Ref } +/** + * @public + */ +export interface TypeHyperlink extends Type {} + /** * @public */ diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 68080ba25e..e7bbb261c7 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -27,6 +27,7 @@ import type { Enum, EnumOf, FullTextData, + Hyperlink, Interface, Obj, PluginConfiguration, @@ -78,6 +79,7 @@ export default plugin(coreId, { Type: '' as Ref>>, TypeString: '' as Ref>>, TypeIntlString: '' as Ref>>, + TypeHyperlink: '' as Ref>>, TypeNumber: '' as Ref>>, TypeMarkup: '' as Ref>>, TypeBoolean: '' as Ref>>, @@ -131,6 +133,7 @@ export default plugin(coreId, { Array: '' as IntlString, Name: '' as IntlString, Enum: '' as IntlString, - Description: '' as IntlString + Description: '' as IntlString, + Hyperlink: '' as IntlString } }) diff --git a/packages/core/src/lang/en.json b/packages/core/src/lang/en.json index da95244e2d..5f481c80ce 100644 --- a/packages/core/src/lang/en.json +++ b/packages/core/src/lang/en.json @@ -24,6 +24,7 @@ "Collection": "Collection", "Array": "Array", "Enum": "Enum", - "Members": "Members" + "Members": "Members", + "Hyperlink": "URL" } } diff --git a/packages/core/src/lang/ru.json b/packages/core/src/lang/ru.json index 48d3575bc6..3f8d12a9fb 100644 --- a/packages/core/src/lang/ru.json +++ b/packages/core/src/lang/ru.json @@ -24,6 +24,7 @@ "Collection": "Коллекция", "Array": "Массив", "Enum": "Справочник", - "Members": "Участники" + "Members": "Участники", + "Hyperlink": "URL" } } diff --git a/packages/model/src/dsl.ts b/packages/model/src/dsl.ts index 7c72bd066e..a44647014e 100644 --- a/packages/model/src/dsl.ts +++ b/packages/model/src/dsl.ts @@ -28,6 +28,7 @@ import core, { Enum, EnumOf, generateId, + Hyperlink, IndexKind, Interface, Markup, @@ -354,6 +355,13 @@ export function TypeString (): Type { return { _class: core.class.TypeString, label: core.string.String } } +/** + * @public + */ +export function TypeHyperlink (): Type { + return { _class: core.class.TypeHyperlink, label: core.string.Hyperlink } +} + /** * @public */ diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index 7053a2f2bb..a03aa076d4 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -518,6 +518,7 @@ input.search { .max-h-60 { max-height: 15rem; } .max-w-30 { max-width: 7.5rem; } .max-w-60 { max-width: 15rem; } +.max-w-80 { max-width: 20rem; } .max-w-240 { max-width: 60rem; } .clear-mins { min-width: 0; diff --git a/packages/ui/src/components/ScrollerBar.svelte b/packages/ui/src/components/ScrollerBar.svelte new file mode 100644 index 0000000000..335cc28c11 --- /dev/null +++ b/packages/ui/src/components/ScrollerBar.svelte @@ -0,0 +1,189 @@ + + + + +
+
+ +
+
+
+
+ + diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index b61b70e9f8..8fb9f1eebe 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -97,6 +97,7 @@ export { default as Submenu } from './components/Submenu.svelte' export { default as TimeShiftPicker } from './components/TimeShiftPicker.svelte' export { default as ErrorPresenter } from './components/ErrorPresenter.svelte' export { default as Scroller } from './components/Scroller.svelte' +export { default as ScrollerBar } from './components/ScrollerBar.svelte' export { default as TabList } from './components/TabList.svelte' export { default as IconAdd } from './components/icons/Add.svelte' diff --git a/plugins/setting-resources/src/components/typeEditors/HyperlinkTypeEditor.svelte b/plugins/setting-resources/src/components/typeEditors/HyperlinkTypeEditor.svelte new file mode 100644 index 0000000000..01f250672b --- /dev/null +++ b/plugins/setting-resources/src/components/typeEditors/HyperlinkTypeEditor.svelte @@ -0,0 +1,25 @@ + + diff --git a/plugins/setting-resources/src/index.ts b/plugins/setting-resources/src/index.ts index 22bcdc2c22..629ef21f85 100644 --- a/plugins/setting-resources/src/index.ts +++ b/plugins/setting-resources/src/index.ts @@ -39,6 +39,7 @@ import EnumTypeEditor from './components/typeEditors/EnumTypeEditor.svelte' import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte' import RefEditor from './components/typeEditors/RefEditor.svelte' import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte' +import HyperlinkTypeEditor from './components/typeEditors/HyperlinkTypeEditor.svelte' import WorkspaceSettings from './components/WorkspaceSettings.svelte' import setting from './plugin' @@ -81,6 +82,7 @@ export default async (): Promise => ({ ManageStatuses, ClassSetting, StringTypeEditor, + HyperlinkTypeEditor, BooleanTypeEditor, NumberTypeEditor, RefEditor, diff --git a/plugins/task-resources/src/components/state/StatesBar.svelte b/plugins/task-resources/src/components/state/StatesBar.svelte index 5bdc4b7815..842e85eab4 100644 --- a/plugins/task-resources/src/components/state/StatesBar.svelte +++ b/plugins/task-resources/src/components/state/StatesBar.svelte @@ -17,8 +17,8 @@ import { Ref, SortingOrder } from '@hcengineering/core' import { createQuery } from '@hcengineering/presentation' import task, { SpaceWithStates, State } from '@hcengineering/task' - import { getPlatformColor } from '@hcengineering/ui' - import { createEventDispatcher, onDestroy, onMount } from 'svelte' + import { getPlatformColor, ScrollerBar } from '@hcengineering/ui' + import { createEventDispatcher } from 'svelte' import StatesBarElement from './StatesBarElement.svelte' import type { StatesBarPosition } from '../..' @@ -27,18 +27,7 @@ export let gap: 'none' | 'small' | 'big' = 'small' let states: State[] = [] - let divScroll: HTMLElement - let divBar: HTMLElement - let isScrolling: boolean = false - let dX: number - let timer: number - - let maskLeft: boolean = false - let maskRight: boolean = false - let mask: 'left' | 'right' | 'both' | 'none' = 'none' - let stepStyle: string - $: stepStyle = gap === 'small' ? 'gap-1' : gap === 'big' ? 'gap-2' : '' const dispatch = createEventDispatcher() @@ -56,77 +45,6 @@ } ) - const checkBar = (): void => { - if (divBar && divScroll) { - const trackW = divScroll.clientWidth - const scrollW = divScroll.scrollWidth - const proc = scrollW / trackW - divBar.style.width = divScroll.clientWidth / proc + 'px' - divBar.style.left = divScroll.scrollLeft / proc + 'px' - if (mask === 'none') divBar.style.visibility = 'hidden' - else { - divBar.style.visibility = 'visible' - if (divBar) { - if (timer) { - clearTimeout(timer) - divBar.style.opacity = '1' - } - timer = setTimeout(() => { - if (divBar) divBar.style.opacity = '0' - }, 2000) - } - } - if (divScroll.clientWidth >= divScroll.scrollWidth) divBar.style.visibility = 'hidden' - } - } - - const onScroll = (event: MouseEvent): void => { - if (isScrolling && divBar && divScroll) { - const rectScroll = divScroll.getBoundingClientRect() - let X = event.clientX - dX - if (X < rectScroll.left) X = rectScroll.left - if (X > rectScroll.right - divBar.clientWidth) X = rectScroll.right - divBar.clientWidth - divBar.style.left = X - rectScroll.x + 'px' - const leftBar = X - rectScroll.x - const widthScroll = rectScroll.width - divBar.clientWidth - const procBar = leftBar / widthScroll - divScroll.scrollLeft = (divScroll.scrollWidth - divScroll.clientWidth) * procBar - } - } - const onScrollEnd = (event: MouseEvent): void => { - const el: HTMLElement = event.currentTarget as HTMLElement - if (el && isScrolling) { - document.removeEventListener('mousemove', onScroll) - document.body.style.userSelect = 'auto' - document.body.style.webkitUserSelect = 'auto' - } - document.removeEventListener('mouseup', onScrollEnd) - isScrolling = false - } - const onScrollStart = (event: MouseEvent): void => { - const el: HTMLElement = event.currentTarget as HTMLElement - if (el && divScroll) { - dX = event.clientX - el.getBoundingClientRect().x - document.addEventListener('mouseup', onScrollEnd) - document.addEventListener('mousemove', onScroll) - document.body.style.userSelect = 'none' - document.body.style.webkitUserSelect = 'none' - isScrolling = true - } - } - - const checkMask = (): void => { - maskLeft = !!(divScroll && divScroll.scrollLeft > 1) - maskRight = !!(divScroll && divScroll.scrollWidth - divScroll.scrollLeft - divScroll.clientWidth > 1) - if (maskLeft || maskRight) { - if (maskLeft && maskRight) mask = 'both' - else if (maskLeft) mask = 'left' - else if (maskRight) mask = 'right' - } else mask = 'none' - - if (!isScrolling) checkBar() - } - const selectItem = (ev: Event, item: State): void => { const el: HTMLElement = ev.currentTarget as HTMLElement const rect = el.getBoundingClientRect() @@ -149,100 +67,18 @@ else if (n === states.length - 1) return 'end' else return 'middle' } - - onMount(() => { - if (divScroll) { - const observer = new IntersectionObserver(() => checkMask(), { root: null, threshold: 0.1 }) - const tempEl = divScroll.querySelector('*') as HTMLElement - if (tempEl) observer.observe(tempEl) - checkMask() - divScroll.addEventListener('scroll', checkMask) - } - }) - onDestroy(() => { - if (divScroll) divScroll.removeEventListener('scroll', checkMask) - }) - const _resize = (): void => checkMask() - -
-
- {#each states as item, i (item._id)} - { - if (item._id !== state) selectItem(ev, item) - }} - /> - {/each} -
-
-
-
- - + + {#each states as item, i (item._id)} + { + if (item._id !== state) selectItem(ev, item) + }} + /> + {/each} + diff --git a/plugins/view-resources/src/components/HyperlinkPresenter.svelte b/plugins/view-resources/src/components/HyperlinkPresenter.svelte new file mode 100644 index 0000000000..177dbb1c16 --- /dev/null +++ b/plugins/view-resources/src/components/HyperlinkPresenter.svelte @@ -0,0 +1,27 @@ + + + +
+ + {value} + +
diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index 09cd223173..74b01d7ba4 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -46,6 +46,7 @@ import RolePresenter from './components/RolePresenter.svelte' import SpacePresenter from './components/SpacePresenter.svelte' import StringEditor from './components/StringEditor.svelte' import StringPresenter from './components/StringPresenter.svelte' +import HyperlinkPresenter from './components/HyperlinkPresenter.svelte' import Table from './components/Table.svelte' import TableBrowser from './components/TableBrowser.svelte' import TimestampPresenter from './components/TimestampPresenter.svelte' @@ -123,6 +124,7 @@ export default async (): Promise => ({ SpacePresenter, StringEditor, StringPresenter, + HyperlinkPresenter, NumberEditor, NumberPresenter, BooleanPresenter,