UBER-417: replace AddSavedView with select popup, allow renaming (#3423)

Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
Vyacheslav Tumanov 2023-06-12 22:07:58 +05:00 committed by GitHub
parent 14b7806967
commit 7c417e2f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 87 deletions

View File

@ -95,6 +95,7 @@
"AddSavedView": "Add saved view", "AddSavedView": "Add saved view",
"Public": "Public", "Public": "Public",
"Hide": "Hide", "Hide": "Hide",
"Rename": "Rename",
"SaveAs": "Save as", "SaveAs": "Save as",
"And": "and", "And": "and",
"Between": "is between", "Between": "is between",

View File

@ -91,6 +91,7 @@
"BetweenDates": "Между датами", "BetweenDates": "Между датами",
"Public": "Публичный", "Public": "Публичный",
"Hide": "Спрятать", "Hide": "Спрятать",
"Rename": "Переименовать",
"SaveAs": "Сохранить как", "SaveAs": "Сохранить как",
"And": "и", "And": "и",
"Between": "между", "Between": "между",

View File

@ -1,75 +0,0 @@
<script lang="ts">
import { getCurrentAccount } from '@hcengineering/core'
import { translate } from '@hcengineering/platform'
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import { deviceOptionsStore, resizeObserver } from '@hcengineering/ui'
import { FilteredView } from '@hcengineering/view'
import { createEventDispatcher, onMount } from 'svelte'
import view from '../../plugin'
export let attachedTo: string | undefined
const me = getCurrentAccount()._id
const q = createQuery()
let views: FilteredView[] = []
const baseQuery = {
attachedTo,
sharable: true,
createdBy: { $ne: me }
}
$: query =
search === ''
? baseQuery
: {
...baseQuery,
name: { $like: `%${search}%` }
}
$: q.query(view.class.FilteredView, query, (res) => {
views = res.filter((p) => !p.users.includes(me))
})
const client = getClient()
const dispatch = createEventDispatcher()
async function add (sv: FilteredView): Promise<void> {
await client.update(sv, { $push: { users: me } })
dispatch('close')
}
let search: string = ''
let phTraslate: string = ''
let searchInput: HTMLInputElement
$: translate(presentation.string.Search, {}).then((res) => {
phTraslate = res
})
onMount(() => {
if (searchInput && !$deviceOptionsStore.isMobile) searchInput.focus()
})
</script>
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
<div class="header">
<input bind:this={searchInput} type="text" bind:value={search} placeholder={phTraslate} />
</div>
<div class="scroll">
<div class="box">
{#each views as value}
<button
class="menu-item no-focus"
on:click={() => {
add(value)
}}
>
<div class="flex-row-center w-full">
{value.name}
</div>
</button>
{/each}
</div>
</div>
</div>

View File

@ -31,12 +31,12 @@
export let selected = false export let selected = false
export let bold = false export let bold = false
export let shortDropbox = false export let shortDropbox = false
export let actions: () => Promise<Action[]> = async () => [] export let actions: (originalEvent?: MouseEvent) => Promise<Action[]> = async () => []
export let indent: 'default' | 'ml-2' | 'ml-4' | 'ml-8' = 'default' export let indent: 'default' | 'ml-2' | 'ml-4' | 'ml-8' = 'default'
let hovered = false let hovered = false
async function onMenuClick (ev: MouseEvent) { async function onMenuClick (ev: MouseEvent) {
showPopup(Menu, { actions: await actions(), ctx: _id }, ev.target as HTMLElement, () => { showPopup(Menu, { actions: await actions(ev), ctx: _id }, ev.target as HTMLElement, () => {
hovered = false hovered = false
}) })
hovered = true hovered = true

View File

@ -22,7 +22,7 @@
export let icon: Asset | undefined = undefined export let icon: Asset | undefined = undefined
export let title: string export let title: string
export let notifications = 0 export let notifications = 0
export let actions: () => Promise<Action[]> = async () => [] export let actions: (originalEvent?: MouseEvent) => Promise<Action[]> = async () => []
export let selected: boolean = false export let selected: boolean = false
export let bold = false export let bold = false
export let indent: 'default' | 'ml-2' | 'ml-4' | 'ml-8' = 'default' export let indent: 'default' | 'ml-2' | 'ml-4' | 'ml-8' = 'default'

View File

@ -108,7 +108,6 @@ import { AggregationMiddleware } from './middleware'
import { grouppingStatusManager, StatusAggregationManager } from './status' import { grouppingStatusManager, StatusAggregationManager } from './status'
export { getActions, invokeAction } from './actions' export { getActions, invokeAction } from './actions'
export { default as ActionHandler } from './components/ActionHandler.svelte' export { default as ActionHandler } from './components/ActionHandler.svelte'
export { default as AddSavedView } from './components/filter/AddSavedView.svelte'
export { default as FilterButton } from './components/filter/FilterButton.svelte' export { default as FilterButton } from './components/filter/FilterButton.svelte'
export { default as FixedColumn } from './components/FixedColumn.svelte' export { default as FixedColumn } from './components/FixedColumn.svelte'
export { default as SourcePresenter } from './components/inference/SourcePresenter.svelte' export { default as SourcePresenter } from './components/inference/SourcePresenter.svelte'

View File

@ -824,6 +824,7 @@ const view = plugin(viewId, {
Timeline: '' as IntlString, Timeline: '' as IntlString,
Public: '' as IntlString, Public: '' as IntlString,
Hide: '' as IntlString, Hide: '' as IntlString,
Rename: '' as IntlString,
Assigned: '' as IntlString, Assigned: '' as IntlString,
Open: '' as IntlString, Open: '' as IntlString,
Created: '' as IntlString, Created: '' as IntlString,

View File

@ -1,11 +1,20 @@
<script lang="ts"> <script lang="ts">
import { Ref, getCurrentAccount } from '@hcengineering/core' import { Ref, getCurrentAccount, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting' import setting from '@hcengineering/setting'
import { Action, IconAdd, Location, eventToHTMLElement, location, navigate, showPopup } from '@hcengineering/ui' import {
Action,
IconAdd,
Location,
eventToHTMLElement,
location,
navigate,
showPopup,
SelectPopup,
getEventPopupPositionElement
} from '@hcengineering/ui'
import view, { Filter, FilteredView, ViewOptions, Viewlet } from '@hcengineering/view' import view, { Filter, FilteredView, ViewOptions, Viewlet } from '@hcengineering/view'
import { import {
AddSavedView,
TreeItem, TreeItem,
TreeNode, TreeNode,
activeViewlet, activeViewlet,
@ -16,11 +25,13 @@
setActiveViewletId, setActiveViewletId,
setFilters, setFilters,
setViewOptions, setViewOptions,
viewOptionStore viewOptionStore,
EditBoxPopup
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { Application } from '@hcengineering/workbench' import { Application } from '@hcengineering/workbench'
import copy from 'fast-copy' import copy from 'fast-copy'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import contact from '@hcengineering/contact'
export let currentApplication: Application | undefined export let currentApplication: Application | undefined
@ -48,8 +59,33 @@
] ]
} }
async function viewAction (filteredView: FilteredView): Promise<Action[]> { async function renameAction (object: FilteredView, originalEvent: MouseEvent | undefined): Promise<Action[]> {
if (filteredView.createdBy === me) return await removeAction(filteredView) return [
{
icon: contact.icon.Edit,
label: view.string.Rename,
action: async (ctx: any, evt: Event) => {
showPopup(
EditBoxPopup,
{ value: object.name, format: 'text' },
getEventPopupPositionElement(originalEvent ?? evt),
async (res) => {
if (res !== undefined) {
await client.update(object, { name: res })
}
}
)
}
}
]
}
async function viewAction (filteredView: FilteredView, originalEvent: MouseEvent | undefined): Promise<Action[]> {
const rename = await renameAction(filteredView, originalEvent)
if (filteredView.createdBy === me) {
const remove = await removeAction(filteredView)
return [...remove, ...rename]
}
return await hideAction(filteredView) return await hideAction(filteredView)
} }
@ -142,11 +178,21 @@
async function getActions (availableFilteredViews: FilteredView[]): Promise<Action[]> { async function getActions (availableFilteredViews: FilteredView[]): Promise<Action[]> {
if (availableFilteredViews.length > 0) { if (availableFilteredViews.length > 0) {
const filteredViewsIdMap = toIdMap(availableFilteredViews)
const pushMeToFV = async (id: Ref<FilteredView>) => {
if (id === undefined) return
const filteredView = filteredViewsIdMap.get(id)
if (filteredView) await client.update(filteredView, { $push: { users: me } })
}
const value = availableFilteredViews.map((p) => ({
id: p._id,
text: p.name
}))
const add: Action = { const add: Action = {
label: view.string.AddSavedView, label: view.string.AddSavedView,
icon: IconAdd, icon: IconAdd,
action: async (_, e): Promise<void> => { action: async (_, e): Promise<void> => {
showPopup(AddSavedView, { attachedTo: currentApplication?.alias }, eventToHTMLElement(e as MouseEvent)) showPopup(SelectPopup, { value, searchable: true }, eventToHTMLElement(e as MouseEvent), pushMeToFV)
} }
} }
return [add] return [add]
@ -164,7 +210,7 @@
title={fv.name} title={fv.name}
selected={selectedId === fv._id} selected={selectedId === fv._id}
on:click={() => load(fv)} on:click={() => load(fv)}
actions={() => viewAction(fv)} actions={(ov) => viewAction(fv, ov)}
/> />
{/each} {/each}
</TreeNode> </TreeNode>