mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-23 10:05:01 +02:00
Add widgets sidebar (#6578)
This commit is contained in:
@@ -65,7 +65,8 @@
|
||||
rootBarExtensions,
|
||||
setResolvedLocation,
|
||||
showPopup,
|
||||
workbenchSeparators
|
||||
workbenchSeparators,
|
||||
mainSeparators
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import {
|
||||
@@ -93,6 +94,8 @@
|
||||
import SelectWorkspaceMenu from './SelectWorkspaceMenu.svelte'
|
||||
import SpaceView from './SpaceView.svelte'
|
||||
import TopMenu from './icons/TopMenu.svelte'
|
||||
import WidgetsBar from './sidebar/Sidebar.svelte'
|
||||
import { sidebarStore, SidebarVariant, syncSidebarState } from '../sidebar'
|
||||
|
||||
let contentPanel: HTMLElement
|
||||
|
||||
@@ -150,6 +153,7 @@
|
||||
$workspacesStore = await getWorkspaceFn()
|
||||
await updateWindowTitle(getLocation())
|
||||
})
|
||||
syncSidebarState()
|
||||
})
|
||||
|
||||
const account = getCurrentAccount() as PersonAccount
|
||||
@@ -607,6 +611,7 @@
|
||||
let lastLoc: Location | undefined = undefined
|
||||
|
||||
defineSeparators('workbench', workbenchSeparators)
|
||||
defineSeparators('main', mainSeparators)
|
||||
|
||||
$: mainNavigator = currentApplication && navigatorModel && navigator && $deviceInfo.navigator.visible
|
||||
$: elementPanel = $deviceInfo.replacedPanel ?? contentPanel
|
||||
@@ -615,6 +620,14 @@
|
||||
person && client.getHierarchy().hasMixin(person, contact.mixin.Employee)
|
||||
? !client.getHierarchy().as(person, contact.mixin.Employee).active
|
||||
: false
|
||||
|
||||
let asideComponent: AnyComponent | undefined
|
||||
|
||||
$: if (asideId !== undefined && navigatorModel !== undefined) {
|
||||
asideComponent = navigatorModel?.aside ?? currentApplication?.aside
|
||||
} else {
|
||||
asideComponent = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if person && deactivated && !isAdminUser()}
|
||||
@@ -852,17 +865,18 @@
|
||||
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
|
||||
{/if}
|
||||
</div>
|
||||
{#if asideId && navigatorModel !== undefined}
|
||||
{@const asideComponent = navigatorModel?.aside ?? currentApplication?.aside}
|
||||
{#if asideComponent !== undefined}
|
||||
<Separator name={'workbench'} index={1} color={'transparent'} separatorSize={0} short />
|
||||
<div class="antiPanel-component antiComponent aside" bind:this={aside}>
|
||||
<Component is={asideComponent} props={{ currentSpace, _id: asideId }} on:close={closeAside} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if asideComponent !== undefined}
|
||||
<Separator name={'workbench'} index={1} color={'transparent'} separatorSize={0} short />
|
||||
<div class="antiPanel-component antiComponent aside" bind:this={aside}>
|
||||
<Component is={asideComponent} props={{ currentSpace, _id: asideId }} on:close={closeAside} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if $sidebarStore.variant === SidebarVariant.EXPANDED}
|
||||
<Separator name={'main'} index={0} color={'transparent'} separatorSize={0} short />
|
||||
{/if}
|
||||
<WidgetsBar />
|
||||
<Dock />
|
||||
<div bind:this={cover} class="cover" />
|
||||
<TooltipInstance />
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { WidgetPreference } from '@hcengineering/workbench'
|
||||
|
||||
import workbench from '../../plugin'
|
||||
import { sidebarStore, SidebarVariant } from '../../sidebar'
|
||||
import WidgetsBarMini from './SidebarMini.svelte'
|
||||
import WidgetsBarExpanded from './SidebarExpanded.svelte'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const widgets = client.getModel().findAllSync(workbench.class.Widget, {})
|
||||
const preferencesQuery = createQuery()
|
||||
|
||||
let preferences: WidgetPreference[] = []
|
||||
$: preferencesQuery.query(workbench.class.WidgetPreference, {}, (res) => {
|
||||
preferences = res
|
||||
})
|
||||
|
||||
$: widgetId = $sidebarStore.widget
|
||||
$: widget = widgets.find((it) => it._id === widgetId)
|
||||
$: size = $sidebarStore.variant === SidebarVariant.MINI ? 'mini' : widget?.size
|
||||
</script>
|
||||
|
||||
<div class="antiPanel-component antiComponent root size-{size}" id="sidebar">
|
||||
{#if $sidebarStore.variant === SidebarVariant.MINI}
|
||||
<WidgetsBarMini {widgets} {preferences} />
|
||||
{:else if $sidebarStore.variant === SidebarVariant.EXPANDED}
|
||||
<WidgetsBarExpanded {widgets} {preferences} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
position: relative;
|
||||
background-color: var(--theme-panel-color);
|
||||
|
||||
&.size-mini {
|
||||
width: 3.5rem !important;
|
||||
min-width: 3.5rem !important;
|
||||
max-width: 3.5rem !important;
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
width: 10rem !important;
|
||||
min-width: 10rem !important;
|
||||
max-width: 10rem !important;
|
||||
}
|
||||
|
||||
&.size-medium {
|
||||
width: 25rem !important;
|
||||
min-width: 25rem !important;
|
||||
max-width: 25rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,172 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget, WidgetPreference, WidgetTab } from '@hcengineering/workbench'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import {
|
||||
Component,
|
||||
resizeObserver,
|
||||
location as locationStore,
|
||||
Location,
|
||||
Header,
|
||||
Breadcrumbs
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
import { closeWidgetTab, sidebarStore, SidebarVariant, WidgetState, openWidgetTab, closeWidget } from '../../sidebar'
|
||||
import WidgetsBar from './widgets/WidgetsBar.svelte'
|
||||
import SidebarTabs from './SidebarTabs.svelte'
|
||||
|
||||
export let widgets: Widget[] = []
|
||||
export let preferences: WidgetPreference[] = []
|
||||
|
||||
let widgetId: Ref<Widget> | undefined = undefined
|
||||
let widget: Widget | undefined = undefined
|
||||
let widgetState: WidgetState | undefined = undefined
|
||||
|
||||
let tabId: string | undefined = undefined
|
||||
let tab: WidgetTab | undefined = undefined
|
||||
let tabs: WidgetTab[] = []
|
||||
|
||||
$: widgetId = $sidebarStore.widget
|
||||
$: widget = widgetId !== undefined ? widgets.find((it) => it._id === widgetId) : undefined
|
||||
$: widgetState = widget !== undefined ? $sidebarStore.widgetsState.get(widget._id) : undefined
|
||||
|
||||
$: tabId = widgetState?.tab
|
||||
$: tabs = widgetState?.tabs ?? []
|
||||
$: tab = tabId !== undefined ? tabs.find((it) => it.id === tabId) ?? tabs[0] : tabs[0]
|
||||
|
||||
$: if ($sidebarStore.widget === undefined) {
|
||||
sidebarStore.update((s) => ({ ...s, variant: SidebarVariant.MINI }))
|
||||
}
|
||||
|
||||
const unsubscribe = locationStore.subscribe((loc: Location) => {
|
||||
if (widget === undefined) return
|
||||
|
||||
for (const tab of tabs) {
|
||||
if (tab.allowedPath !== undefined && !tab.isPinned) {
|
||||
const path = loc.path.join('/')
|
||||
if (!path.startsWith(tab.allowedPath)) {
|
||||
void handleTabClose(tab.id, widget)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
async function handleTabClose (tabId: string, widget?: Widget): Promise<void> {
|
||||
if (widget === undefined) return
|
||||
await closeWidgetTab(widget, tabId)
|
||||
}
|
||||
|
||||
function handleTabOpen (tabId: string, widget?: Widget): void {
|
||||
if (widget === undefined) return
|
||||
openWidgetTab(widget._id, tabId)
|
||||
}
|
||||
|
||||
let componentHeight = '0px'
|
||||
let componentWidth = '0px'
|
||||
|
||||
function resize (element: Element): void {
|
||||
componentHeight = `${element.clientHeight}px`
|
||||
componentWidth = `${element.clientWidth}px`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="content">
|
||||
{#if widget?.component}
|
||||
<div class="component" use:resizeObserver={resize}>
|
||||
{#if widget.headerLabel}
|
||||
<Header
|
||||
allowFullsize={false}
|
||||
type="type-aside"
|
||||
hideBefore={true}
|
||||
hideActions={false}
|
||||
hideDescription={true}
|
||||
adaptive="disabled"
|
||||
on:close={() => {
|
||||
if (widget !== undefined) {
|
||||
closeWidget(widget._id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Breadcrumbs items={[{ label: widget.headerLabel }]} currentOnly />
|
||||
</Header>
|
||||
{/if}
|
||||
<Component
|
||||
is={widget?.component}
|
||||
props={{ tab, widgetState, height: componentHeight, width: componentWidth, widget }}
|
||||
on:close={() => {
|
||||
if (widget !== undefined) {
|
||||
closeWidget(widget._id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if widget !== undefined && tabs.length > 0}
|
||||
<SidebarTabs
|
||||
{tabs}
|
||||
selected={tab?.id}
|
||||
{widget}
|
||||
on:close={(e) => {
|
||||
void handleTabClose(e.detail, widget)
|
||||
}}
|
||||
on:open={(e) => {
|
||||
handleTabOpen(e.detail, widget)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<WidgetsBar {widgets} {preferences} selected={widgetId} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
overflow: auto;
|
||||
|
||||
flex: 1;
|
||||
width: calc(100% - 3.5rem);
|
||||
height: 100%;
|
||||
background: var(--theme-panel-color);
|
||||
|
||||
border-right: 1px solid var(--global-ui-BorderColor);
|
||||
border-top-left-radius: var(--small-focus-BorderRadius);
|
||||
border-bottom-left-radius: var(--small-focus-BorderRadius);
|
||||
}
|
||||
|
||||
.component {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget, WidgetPreference } from '@hcengineering/workbench'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
|
||||
import WidgetsBar from './widgets/WidgetsBar.svelte'
|
||||
|
||||
export let widgets: Widget[] = []
|
||||
export let preferences: WidgetPreference[] = []
|
||||
export let selected: Ref<Widget> | undefined = undefined
|
||||
</script>
|
||||
|
||||
<WidgetsBar {widgets} {preferences} {selected} />
|
||||
@@ -0,0 +1,57 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Action, Menu, ModernTab, showPopup } from '@hcengineering/ui'
|
||||
import { Widget, WidgetTab } from '@hcengineering/workbench'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let tab: WidgetTab
|
||||
export let widget: Widget
|
||||
export let selected = false
|
||||
export let actions: Action[] = []
|
||||
|
||||
$: icon = tab.icon ?? widget.icon
|
||||
|
||||
$: if (tab.iconComponent) {
|
||||
void getResource(tab.iconComponent).then((res) => {
|
||||
icon = res
|
||||
})
|
||||
}
|
||||
|
||||
function handleMenu (event: MouseEvent): void {
|
||||
if (actions.length === 0) {
|
||||
return
|
||||
}
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
showPopup(Menu, { actions }, event.target as HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModernTab
|
||||
label={tab.name}
|
||||
labelIntl={tab.nameIntl ?? widget.label}
|
||||
highlighted={selected}
|
||||
orientation="vertical"
|
||||
kind={tab.isPinned ? 'secondary' : 'primary'}
|
||||
{icon}
|
||||
iconProps={tab.iconProps}
|
||||
canClose={!tab.isPinned}
|
||||
maxSize="13.5rem"
|
||||
on:close
|
||||
on:click
|
||||
on:contextmenu={handleMenu}
|
||||
/>
|
||||
@@ -0,0 +1,99 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget, WidgetTab } from '@hcengineering/workbench'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { Action, Component } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import SidebarTab from './SidebarTab.svelte'
|
||||
import { closeWidgetTab, pinWidgetTab, unpinWidgetTab } from '../../sidebar'
|
||||
|
||||
export let tabs: WidgetTab[] = []
|
||||
export let widget: Widget
|
||||
export let selected: string | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function getActions (tab: WidgetTab): Action[] {
|
||||
const pinAction: Action = {
|
||||
label: view.string.Pin,
|
||||
icon: view.icon.Pin,
|
||||
action: async () => {
|
||||
pinWidgetTab(widget, tab.id)
|
||||
}
|
||||
}
|
||||
const unpinAction: Action = {
|
||||
label: view.string.Unpin,
|
||||
icon: view.icon.Pin,
|
||||
action: async () => {
|
||||
unpinWidgetTab(widget, tab.id)
|
||||
}
|
||||
}
|
||||
const closeAction: Action = {
|
||||
label: presentation.string.Close,
|
||||
icon: view.icon.Delete,
|
||||
action: async () => {
|
||||
void closeWidgetTab(widget, tab.id)
|
||||
}
|
||||
}
|
||||
return [tab.isPinned ? unpinAction : pinAction, closeAction]
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="tabs">
|
||||
{#each tabs as tab}
|
||||
{#if widget.tabComponent}
|
||||
<Component
|
||||
is={widget.tabComponent}
|
||||
props={{ tab, widget, selected: tab.id === selected, actions: getActions(tab) }}
|
||||
on:close={() => dispatch('close', tab.id)}
|
||||
on:click={() => {
|
||||
dispatch('open', tab.id)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<SidebarTab
|
||||
{tab}
|
||||
{widget}
|
||||
actions={getActions(tab)}
|
||||
selected={tab.id === selected}
|
||||
on:close={() => dispatch('close', tab.id)}
|
||||
on:click={() => {
|
||||
dispatch('open', tab.id)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
max-width: 30px;
|
||||
flex: 1;
|
||||
border-right: 1px solid var(--theme-divider-color);
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
gap: 0.25rem;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget, WidgetPreference, WidgetType } from '@hcengineering/workbench'
|
||||
import { CheckBox, Grid, Modal } from '@hcengineering/ui'
|
||||
import core, { Ref } from '@hcengineering/core'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import WidgetPresenter from './WidgetPresenter.svelte'
|
||||
import workbench from '../../../plugin'
|
||||
|
||||
export let widgets: Widget[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const preferencesQuery = createQuery()
|
||||
|
||||
const widgetsUpdates = new Map<Ref<Widget>, boolean>()
|
||||
|
||||
let preferences: WidgetPreference[] = []
|
||||
$: preferencesQuery.query(workbench.class.WidgetPreference, {}, (res) => {
|
||||
preferences = res
|
||||
})
|
||||
|
||||
function handleClose (): void {
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function isEnabled (
|
||||
widget: Widget,
|
||||
widgetsUpdates: Map<Ref<Widget>, boolean>,
|
||||
preference?: WidgetPreference
|
||||
): boolean {
|
||||
return widgetsUpdates.get(widget._id) ?? preference?.enabled ?? false
|
||||
}
|
||||
|
||||
function handleCheck (widget: Widget, preference?: WidgetPreference): void {
|
||||
const newValue = !isEnabled(widget, widgetsUpdates, preference)
|
||||
|
||||
widgetsUpdates.set(widget._id, newValue)
|
||||
}
|
||||
|
||||
async function handleApply (): Promise<void> {
|
||||
for (const [widget, enabled] of widgetsUpdates) {
|
||||
const preference = preferences.find((it) => it.attachedTo === widget)
|
||||
if (preference !== undefined) {
|
||||
await client.diffUpdate(preference, { enabled })
|
||||
} else {
|
||||
await client.createDoc(workbench.class.WidgetPreference, core.space.Workspace, { attachedTo: widget, enabled })
|
||||
}
|
||||
}
|
||||
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
label={workbench.string.ConfigureWidgets}
|
||||
type="type-popup"
|
||||
okLabel={presentation.string.Save}
|
||||
okAction={handleApply}
|
||||
canSave={true}
|
||||
onCancel={handleClose}
|
||||
on:close
|
||||
>
|
||||
<Grid column={1} rowGap={0.5}>
|
||||
{#each widgets as widget}
|
||||
{#if widget.type === WidgetType.Configurable}
|
||||
{@const preference = preferences.find((it) => it.attachedTo === widget._id)}
|
||||
<div class="item">
|
||||
<CheckBox
|
||||
size="small"
|
||||
checked={isEnabled(widget, widgetsUpdates, preference)}
|
||||
on:value={() => {
|
||||
handleCheck(widget, preference)
|
||||
}}
|
||||
/>
|
||||
<WidgetPresenter {widget} withLabel />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</Grid>
|
||||
</Modal>
|
||||
|
||||
<style lang="scss">
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget } from '@hcengineering/workbench'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
|
||||
import AppItem from '../../AppItem.svelte'
|
||||
|
||||
export let widget: Widget
|
||||
export let withLabel = false
|
||||
export let highlighted = false
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<AppItem label={widget.label} icon={widget.icon} selected={highlighted} size="small" on:click />
|
||||
{#if withLabel}
|
||||
<Label label={widget.label} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Widget, WidgetPreference, WidgetType } from '@hcengineering/workbench'
|
||||
import { IconSettings, ModernButton, showPopup } from '@hcengineering/ui'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
|
||||
import WidgetPresenter from './/WidgetPresenter.svelte'
|
||||
import AddWidgetsPopup from './AddWidgetsPopup.svelte'
|
||||
import { openWidget, sidebarStore, SidebarVariant } from '../../../sidebar'
|
||||
|
||||
export let widgets: Widget[] = []
|
||||
export let preferences: WidgetPreference[] = []
|
||||
export let selected: Ref<Widget> | undefined = undefined
|
||||
|
||||
function handleAddWidget (): void {
|
||||
showPopup(AddWidgetsPopup, { widgets })
|
||||
}
|
||||
|
||||
function handleSelectWidget (widget: Widget): void {
|
||||
if (selected === widget._id) {
|
||||
sidebarStore.update((state) => ({ ...state, widget: undefined, variant: SidebarVariant.MINI }))
|
||||
} else {
|
||||
openWidget(widget)
|
||||
}
|
||||
}
|
||||
|
||||
$: fixedWidgets = widgets.filter((widget) => widget.type === WidgetType.Fixed)
|
||||
$: flexibleWidgets = widgets.filter(
|
||||
(widget) => widget.type === WidgetType.Flexible && $sidebarStore.widgetsState.has(widget._id)
|
||||
)
|
||||
$: configurableWidgets = preferences
|
||||
.filter((it) => it.enabled)
|
||||
.sort((a, b) => a.modifiedOn - b.modifiedOn)
|
||||
.map((it) => widgets.find((widget) => widget._id === it.attachedTo))
|
||||
.filter((widget): widget is Widget => widget !== undefined && widget.type === WidgetType.Configurable)
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="block">
|
||||
{#each fixedWidgets as widget}
|
||||
<WidgetPresenter
|
||||
{widget}
|
||||
highlighted={widget._id === selected}
|
||||
on:click={() => {
|
||||
handleSelectWidget(widget)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#if configurableWidgets.length > 0}
|
||||
<div class="separator" />
|
||||
{#each configurableWidgets as widget}
|
||||
<WidgetPresenter
|
||||
{widget}
|
||||
highlighted={widget._id === selected}
|
||||
on:click={() => {
|
||||
handleSelectWidget(widget)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if flexibleWidgets.length > 0}
|
||||
<div class="separator" />
|
||||
{#each flexibleWidgets as widget}
|
||||
<WidgetPresenter
|
||||
{widget}
|
||||
highlighted={widget._id === selected}
|
||||
on:click={() => {
|
||||
handleSelectWidget(widget)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if widgets.some((widget) => widget.type === WidgetType.Configurable)}
|
||||
<div class="separator" />
|
||||
<ModernButton icon={IconSettings} size="small" on:click={handleAddWidget} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
padding: 0.5rem 0;
|
||||
width: 3.5rem;
|
||||
min-width: 3.5rem;
|
||||
max-width: 3.5rem;
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 2rem;
|
||||
height: 1px;
|
||||
background-color: var(--global-ui-BorderColor);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user