Chunter browser: common, channels, contact (#2011)

Signed-off-by: budaeva <irina.budaeva@xored.com>
This commit is contained in:
budaeva 2022-06-10 23:10:15 +07:00 committed by GitHub
parent f473bdbb57
commit b471284c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 392 additions and 106 deletions

View File

@ -308,7 +308,7 @@ export function createModel (builder: Builder): void {
{
id: 'spaceBrowser',
component: workbench.component.SpaceBrowser,
icon: workbench.icon.Search,
icon: chunter.icon.Hashtag,
label: chunter.string.ChannelBrowser,
position: 'top',
spaceClass: chunter.class.Channel,
@ -351,10 +351,11 @@ export function createModel (builder: Builder): void {
}
},
{
id: 'messagesBrowser',
label: chunter.string.MessagesBrowser,
component: chunter.component.MessagesBrowser,
visibleIf: chunter.function.MessageBrowserVisible
id: 'chunterBrowser',
label: chunter.string.ChunterBrowser,
icon: workbench.icon.Search,
component: chunter.component.ChunterBrowser,
visibleIf: chunter.function.ChunterBrowserVisible
}
],
spaces: [
@ -449,6 +450,10 @@ export function createModel (builder: Builder): void {
builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, {
filters: ['space', 'modifiedOn', 'createBy', '_class']
})
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ClassFilters, {
filters: ['private', 'archived']
})
}
export { chunterOperation } from './migration'

View File

@ -30,7 +30,7 @@ export default mergeIds(chunterId, chunter, {
Threads: '' as AnyComponent,
ThreadView: '' as AnyComponent,
SavedMessages: '' as AnyComponent,
MessagesBrowser: '' as AnyComponent
ChunterBrowser: '' as AnyComponent
},
action: {
MarkCommentUnread: '' as Ref<Action>,
@ -86,6 +86,6 @@ export default mergeIds(chunterId, chunter, {
Random: '' as Ref<Channel>
},
function: {
MessageBrowserVisible: '' as Resource<(spaces: Space[]) => boolean>
ChunterBrowserVisible: '' as Resource<(spaces: Space[]) => boolean>
}
})

View File

@ -354,6 +354,7 @@ input.search {
.mr-6 { margin-right: 1.5rem; }
.mr-8 { margin-right: 2rem; }
.mr-10 { margin-right: 2.5rem; }
.mr-32 { margin-right: 8rem }
.mt-0-5 { margin-top: .125rem; }
.mt-1 { margin-top: .25rem; }
.mt-2 { margin-top: .5rem; }
@ -396,6 +397,7 @@ input.search {
.pb-3 { padding-bottom: .75rem; }
.pb-4 { padding-bottom: 1rem; }
.pb-6 { padding-bottom: 1.5rem; }
.pb-16 { padding-bottom: 4rem; }
.px-2 { padding: 0 .5rem; }
.px-3 { padding: 0 .75rem; }
.px-4 { padding: 0 1rem; }
@ -464,6 +466,7 @@ input.search {
.w-16 { width: 4rem; }
.w-22 { width: 5.5rem; }
.w-24 { width: 6rem; }
.w-32 { width: 8rem; }
.w-60 { width: 15rem; }
.w-85 { width: 21.25rem; }
.w-165 { width: 41.25rem; }

View File

@ -35,6 +35,8 @@
import FileBrowserFilters from './FileBrowserFilters.svelte'
import FileBrowserSortMenu from './FileBrowserSortMenu.svelte'
export let withHeader: boolean = true
const client = getClient()
const loc = getCurrentLocation()
const spaceId: Ref<Space> | undefined = loc.query?.spaceId as Ref<Space> | undefined
@ -48,7 +50,7 @@
const currentUser = getCurrentAccount() as EmployeeAccount
let selectedParticipants: Ref<Employee>[] = [currentUser.employee]
let selectedSpaces: Ref<Space>[] = []
let searchQuery: string = ''
export let search: string = ''
let isLoading = false
let attachments: Attachment[] = []
@ -58,7 +60,7 @@
let selectedFileTypeId = 'typeAny'
let isListDisplayMode = true
$: fetch(searchQuery, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
$: fetch(search, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
async function fetch (
searchQuery_: string,
@ -108,12 +110,14 @@
}
</script>
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
{#if withHeader}
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
</div>
<EditWithIcon icon={IconSearch} bind:value={search} placeholder={ui.string.SearchDots} />
</div>
<EditWithIcon icon={IconSearch} bind:value={searchQuery} placeholder={ui.string.SearchDots} />
</div>
{/if}
<div class="ac-header full">
<FileBrowserFilters
{requestedSpaceClasses}

View File

@ -43,7 +43,8 @@ export {
AttachmentRefInput,
AttachmentList,
AttachmentDocList,
FileDownload
FileDownload,
FileBrowser
}
export enum FileBrowserSortMode {

View File

@ -62,6 +62,9 @@
"ConvertToPrivate": "Convert to private channel",
"MessagesBrowser": "Messages browser",
"CreateBy": "Create by",
"ThreadMessage": "Thread message"
"ThreadMessage": "Thread message",
"ChunterBrowser": "Search",
"Messages": "Messages",
"NoResults": "No results"
}
}

View File

@ -61,6 +61,9 @@
"ConvertToPrivate": "Конвертировать в закрытый канал",
"MessagesBrowser": "Браузер сообщений",
"CreateBy": "Создано пользователем",
"ThreadMessage": "Сообщение в обсуждении"
"ThreadMessage": "Сообщение в обсуждении",
"ChunterBrowser": "Поиск",
"Messages": "Сообщения",
"NoResults": "Нет результатов"
}
}

View File

@ -49,6 +49,7 @@
"@anticrm/view-resources": "~0.6.0",
"@anticrm/view": "~0.6.0",
"@anticrm/workbench": "~0.6.1",
"@anticrm/preference": "~0.6.0"
"@anticrm/preference": "~0.6.0",
"@anticrm/workbench-resources": "~0.6.1"
}
}

View File

@ -0,0 +1,134 @@
<script lang="ts">
import attachment from '@anticrm/attachment'
import { FileBrowser } from '@anticrm/attachment-resources'
import { Button } from '@anticrm/ui'
import workbench from '@anticrm/workbench'
import { SpaceBrowser } from '@anticrm/workbench-resources'
import Header from './Header.svelte'
import contact from '@anticrm/contact-resources/src/plugin'
import { EmployeeBrowser } from '@anticrm/contact-resources'
import { userSearch } from '../index'
import plugin from '../plugin'
import { SearchType } from '../utils'
import MessagesBrowser from './MessagesBrowser.svelte'
import { FilterButton } from '@anticrm/view-resources'
import { Filter } from '@anticrm/view'
let userSearch_: string = ''
userSearch.subscribe((v) => (userSearch_ = v))
let searchType: SearchType = SearchType.Messages
const components = [
{ searchType: SearchType.Messages, component: MessagesBrowser, filterClass: plugin.class.ChunterMessage },
{
searchType: SearchType.Channels,
component: SpaceBrowser,
filterClass: plugin.class.Channel,
props: {
_class: plugin.class.Channel,
label: plugin.string.ChannelBrowser,
withFilterButton: false
}
},
{
searchType: SearchType.Files,
component: FileBrowser,
props: {
requestedSpaceClasses: [plugin.class.Channel, plugin.class.DirectMessage]
}
},
{ searchType: SearchType.Contacts, component: EmployeeBrowser, filterClass: contact.class.Employee }
]
let filters: Filter[] = []
</script>
<div class="flex-col h-full">
<div class="ac-header divide full">
<Header icon={workbench.icon.Search} intlLabel={plugin.string.ChunterBrowser} />
</div>
<div class="h-full browser">
<div class="pb-16 component">
<div class="h-full">
{#if components[searchType].component}
<svelte:component
this={components[searchType].component}
withHeader={false}
bind:search={userSearch_}
bind:filters
{...components[searchType].props}
/>
{/if}
</div>
</div>
<div class="p-3 bar">
<div class="w-32 flex-center"><FilterButton _class={components[searchType].filterClass} bind:filters /></div>
<div class="flex-center w-full mr-32 buttons">
<div class="ml-1 p-1 btn">
<Button
label={plugin.string.Messages}
selected={searchType === SearchType.Messages}
kind="transparent"
on:click={() => {
searchType = SearchType.Messages
}}
/>
</div>
<div class="ml-1 p-1 btn">
<Button
label={plugin.string.Channels}
kind="transparent"
selected={searchType === SearchType.Channels}
on:click={() => {
searchType = SearchType.Channels
}}
/>
</div>
<div class="ml-1 p-1 btn">
<Button
label={attachment.string.Files}
kind="transparent"
selected={searchType === SearchType.Files}
on:click={() => {
searchType = SearchType.Files
filters = []
}}
/>
</div>
<div class="ml-1 p-1 btn">
<Button
kind="transparent"
label={contact.string.Contacts}
selected={searchType === SearchType.Contacts}
on:click={() => {
searchType = SearchType.Contacts
filters = []
}}
/>
</div>
</div>
</div>
</div>
</div>
<style lang="scss">
.browser {
flex-grow: 2;
display: flex;
justify-content: start;
flex-direction: column-reverse;
}
.bar {
flex-grow: 1;
display: flex;
justify-content: start;
max-height: 4rem;
}
.component {
flex-grow: 2;
height: 0;
}
</style>

View File

@ -18,9 +18,9 @@
import { getCurrentAccount } from '@anticrm/core'
import { createQuery, getClient, CombineAvatars } from '@anticrm/presentation'
import contact, { EmployeeAccount } from '@anticrm/contact'
import { getCurrentLocation, navigate, SearchEdit, showPanel } from '@anticrm/ui'
import { SearchEdit, showPanel } from '@anticrm/ui'
import chunter from '../plugin'
import { getDmName } from '../utils'
import { getDmName, navigateToSpecial } from '../utils'
import { userSearch } from '../index'
export let spaceId: Ref<DirectMessage> | undefined
@ -72,9 +72,7 @@
userSearch.set(ev.detail)
if (ev.detail !== '') {
const loc = getCurrentLocation()
loc.path[2] = 'messagesBrowser'
navigate(loc)
navigateToSpecial('chunterBrowser')
}
}}
/>

View File

@ -13,13 +13,15 @@
// limitations under the License.
-->
<script lang="ts">
import type { Asset } from '@anticrm/platform'
import { AnySvelteComponent, getCurrentLocation, Icon, navigate, SearchEdit } from '@anticrm/ui'
import type { Asset, IntlString } from '@anticrm/platform'
import { AnySvelteComponent, Icon, Label, SearchEdit } from '@anticrm/ui'
import { userSearch } from '../index'
import { navigateToSpecial } from '../utils'
export let icon: Asset | AnySvelteComponent | undefined
export let label: string
export let description: string | undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let label: string | undefined = undefined
export let intlLabel: IntlString | undefined = undefined
export let description: string | undefined = undefined
let userSearch_: string
userSearch.subscribe((v) => (userSearch_ = v))
@ -28,7 +30,13 @@
<div class="ac-header__wrap-description">
<div class="ac-header__wrap-title" on:click>
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
<span class="ac-header__title">{label}</span>
{#if label}
<span class="ac-header__title">{label}</span>
{:else if intlLabel}
<div class="ac-header__title">
<Label label={intlLabel} />
</div>
{/if}
</div>
{#if description}<span class="ac-header__description">{description}</span>{/if}
</div>
@ -38,9 +46,7 @@
userSearch.set(ev.detail)
if (ev.detail !== '') {
const loc = getCurrentLocation()
loc.path[2] = 'messagesBrowser'
navigate(loc)
navigateToSpecial('chunterBrowser')
}
}}
/>

View File

@ -6,34 +6,34 @@
import { createQuery, getClient } from '@anticrm/presentation'
import { Label, Scroller, SearchEdit } from '@anticrm/ui'
import type { Filter } from '@anticrm/view'
import { FilterBar, FilterButton } from '@anticrm/view-resources'
import { FilterBar } from '@anticrm/view-resources'
import MessageComponent from './Message.svelte'
import { userSearch } from '../index'
import plugin from '../plugin'
import { openMessageFromSpecial } from '../utils'
let userSearch_: string = ''
userSearch.subscribe((v) => (userSearch_ = v))
export let withHeader: boolean = true
let searchQuery: DocumentQuery<ChunterMessage> = { $search: userSearch_ }
export let search: string = ''
let filters: Filter[] = []
let searchQuery: DocumentQuery<ChunterMessage> = { $search: search }
export let filters: Filter[] = []
function updateSearchQuery (search: string): void {
searchQuery = { $search: search }
}
$: updateSearchQuery(userSearch_)
$: updateSearchQuery(search)
const client = getClient()
const _class = chunter.class.ChunterMessage
export let filterClass = chunter.class.ChunterMessage
let messages: ChunterMessage[] = []
let resultQuery: DocumentQuery<ChunterMessage> = { ...searchQuery }
async function updateMessages (resultQuery: DocumentQuery<ChunterMessage>) {
messages = await client.findAll(
_class,
filterClass,
{
...resultQuery
},
@ -97,32 +97,38 @@
})
</script>
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={plugin.string.MessagesBrowser} /></span>
</div>
<div class="ml-4"><FilterButton {_class} bind:filters /></div>
<SearchEdit
value={userSearch_}
on:change={(ev) => {
userSearch.set(ev.detail)
updateSearchQuery(userSearch_)
updateMessages(resultQuery)
}}
/>
</div>
<FilterBar {_class} query={searchQuery} bind:filters on:change={(e) => (resultQuery = e.detail)} />
<Scroller>
{#each messages as message}
<div on:click={() => openMessageFromSpecial(message)}>
<MessageComponent
{message}
{employees}
on:openThread
isPinned={pinnedIds.includes(message._id)}
isSaved={savedMessagesIds.includes(message._id)}
{savedAttachmentsIds}
/>
{#if withHeader}
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={plugin.string.MessagesBrowser} /></span>
</div>
{/each}
</Scroller>
<SearchEdit
value={search}
on:change={() => {
updateSearchQuery(search)
updateMessages(resultQuery)
}}
/>
</div>
{/if}
<FilterBar _class={filterClass} query={searchQuery} bind:filters on:change={(e) => (resultQuery = e.detail)} />
{#if messages.length > 0}
<Scroller>
{#each messages as message}
<div on:click={() => openMessageFromSpecial(message)}>
<MessageComponent
{message}
{employees}
on:openThread
isPinned={pinnedIds.includes(message._id)}
isSaved={savedMessagesIds.includes(message._id)}
{savedAttachmentsIds}
/>
</div>
{/each}
</Scroller>
{:else}
<div class="flex-center h-full text-lg">
<Label label={plugin.string.NoResults} />
</div>
{/if}

View File

@ -34,7 +34,7 @@ import CommentsPresenter from './components/CommentsPresenter.svelte'
import CreateChannel from './components/CreateChannel.svelte'
import CreateDirectMessage from './components/CreateDirectMessage.svelte'
import EditChannel from './components/EditChannel.svelte'
import MessagesBrowser from './components/MessagesBrowser.svelte'
import ChunterBrowser from './components/ChunterBrowser.svelte'
import ThreadView from './components/ThreadView.svelte'
import Threads from './components/Threads.svelte'
import SavedMessages from './components/SavedMessages.svelte'
@ -176,7 +176,7 @@ export async function DeleteMessageFromSaved (message: ChunterMessage): Promise<
export const userSearch = writable('')
export function messageBrowserVisible (spaces: Space[]): boolean {
export function chunterBrowserVisible (spaces: Space[]): boolean {
return false
}
@ -186,21 +186,21 @@ export default async (): Promise<Resources> => ({
CreateChannel,
CreateDirectMessage,
ChannelHeader,
DmHeader,
ChannelView,
CommentPresenter,
CommentsPresenter,
ChannelPresenter,
ChunterBrowser,
DmHeader,
DmPresenter,
EditChannel,
MessagesBrowser,
Threads,
ThreadView,
SavedMessages
},
function: {
GetDmName: getDmName,
MessageBrowserVisible: messageBrowserVisible
ChunterBrowserVisible: chunterBrowserVisible
},
activity: {
TxCommentCreate,

View File

@ -82,6 +82,9 @@ export default mergeIds(chunterId, chunter, {
ChannelBrowser: '' as IntlString,
SavedItems: '' as IntlString,
AddMembersHeader: '' as IntlString,
MessagesBrowser: '' as IntlString
MessagesBrowser: '' as IntlString,
ChunterBrowser: '' as IntlString,
Messages: '' as IntlString,
NoResults: '' as IntlString
}
})

View File

@ -84,3 +84,16 @@ export function openMessageFromSpecial (message: ChunterMessage): void {
}
navigate(loc)
}
export function navigateToSpecial (specialId: string): void {
const loc = getCurrentLocation()
loc.path[2] = specialId
navigate(loc)
}
export enum SearchType {
Messages,
Channels,
Files,
Contacts
}

View File

@ -0,0 +1,56 @@
<script lang="ts">
import contact, { Employee } from '@anticrm/contact'
import { DocumentQuery, SortingOrder } from '@anticrm/core'
import { getClient } from '@anticrm/presentation'
import { Scroller } from '@anticrm/ui'
import EmployeePresenter from './EmployeePresenter.svelte'
export let search: string = ''
const client = getClient()
$: searchQuery = search.length ? { $search: search } : {}
$: resultQuery = { ...searchQuery }
let employees: Employee[] = []
async function updateEmployees (resultQuery: DocumentQuery<Employee>) {
employees = await client.findAll(
contact.class.Employee,
{
...resultQuery
},
{
sort: { createOn: SortingOrder.Descending },
limit: 100,
lookup: { _id: { statuses: contact.class.Status } }
}
)
}
$: updateEmployees(resultQuery)
</script>
<div class="container">
<Scroller>
<div>
{#each employees as employee}
<div class="fs-title item">
<EmployeePresenter value={employee} avatarSize="medium" />
</div>
{/each}
</div>
</Scroller>
</div>
<style lang="scss">
.container {
border-top: 1px solid var(--divider-color);
}
.item {
color: var(--caption-color);
padding: 0.5rem 2rem;
&:hover,
&:focus {
background-color: var(--popup-bg-hover);
}
}
</style>

View File

@ -13,6 +13,7 @@
export let shouldShowName: boolean = true
export let shouldShowPlaceholder = false
export let onEmployeeEdit: ((event: MouseEvent) => void) | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
let container: HTMLElement
@ -39,6 +40,7 @@
onEdit={handlePersonEdit}
{shouldShowAvatar}
{shouldShowName}
{avatarSize}
{shouldShowPlaceholder}
/>
</div>

View File

@ -36,6 +36,7 @@ import PersonPresenter from './components/PersonPresenter.svelte'
import SocialEditor from './components/SocialEditor.svelte'
import contact from './plugin'
import EmployeePresenter from './components/EmployeePresenter.svelte'
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
import OrganizationEditor from './components/OrganizationEditor.svelte'
import PersonEditor from './components/PersonEditor.svelte'
@ -53,6 +54,7 @@ export {
OrganizationSelector,
ChannelsDropdown,
EmployeePresenter,
EmployeeBrowser,
MemberPresenter
}

View File

@ -71,10 +71,7 @@
$: saveFilters(filters)
function saveFilters (filters: Filter[]) {
const loc = getCurrentLocation()
loc.fragment = undefined
loc.query = undefined
const key = 'filter' + locationToUrl(loc)
const key = makeKey(_class)
if (filters.length > 0) {
localStorage.setItem(key, JSON.stringify(filters))
} else {
@ -87,10 +84,7 @@
function load (_class: Ref<Class<Doc>>) {
loading = true
const oldFilters = filters
const loc = getCurrentLocation()
loc.fragment = undefined
loc.query = undefined
const key = 'filter' + locationToUrl(loc)
const key = makeKey(_class)
const saved = localStorage.getItem(key)
if (saved !== null) {
filters = JSON.parse(saved)
@ -101,6 +95,13 @@
oldFilters.forEach((p) => p.onRemove?.())
}
function makeKey (_class: Ref<Class<Doc>>): string {
const loc = getCurrentLocation()
loc.fragment = undefined
loc.query = undefined
return 'filter' + locationToUrl(loc) + _class
}
async function makeQuery (query: DocumentQuery<Doc>, filters: Filter[]): Promise<void> {
const newQuery = hierarchy.clone(query)
for (let i = 0; i < filters.length; i++) {

View File

@ -20,7 +20,7 @@
import view from '../../plugin'
import FilterTypePopup from './FilterTypePopup.svelte'
export let _class: Ref<Class<Doc>>
export let _class: Ref<Class<Doc>> | undefined
export let filters: Filter[]
const client = getClient()
@ -44,8 +44,13 @@
)
}
$: clazz = hierarchy.getClass(_class)
$: visible = hierarchy.hasMixin(clazz, view.mixin.ClassFilters)
let visible: boolean
$: {
if (_class) {
const clazz = hierarchy.getClass(_class)
visible = hierarchy.hasMixin(clazz, view.mixin.ClassFilters)
} else visible = false
}
</script>
{#if visible}

View File

@ -143,7 +143,7 @@
<div class="check pointer-events-none">
<CheckBox checked={isSelected(value, selectedValues)} primary />
</div>
{#if value}
{#if value !== undefined}
<svelte:component
this={attribute.presenter}
value={typeof value === 'string' ? realValue : value}

View File

@ -13,7 +13,16 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, FindOptions, getCurrentAccount, Ref, SortingOrder, SortingQuery, Space } from '@anticrm/core'
import {
Class,
DocumentQuery,
FindOptions,
getCurrentAccount,
Ref,
SortingOrder,
SortingQuery,
Space
} from '@anticrm/core'
import {
AnyComponent,
Button,
@ -27,35 +36,44 @@
} from '@anticrm/ui'
import presentation, { createQuery, getClient } from '@anticrm/presentation'
import plugin from '../plugin'
import { SpacePresenter } from '@anticrm/view-resources'
import { FilterBar, FilterButton, SpacePresenter } from '@anticrm/view-resources'
import { IntlString } from '@anticrm/platform'
import { classIcon } from '../utils'
import { Filter } from '@anticrm/view'
export let _class: Ref<Class<Space>>
export let label: IntlString
export let createItemDialog: AnyComponent | undefined
export let createItemLabel: IntlString = presentation.string.Create
export let withHeader: boolean = true
export let withFilterButton: boolean = true
const me = getCurrentAccount()._id
const client = getClient()
const spaceQuery = createQuery()
let search: string = ''
export let search: string = ''
const sort: SortingQuery<Space> = {
name: SortingOrder.Ascending
}
export let filters: Filter[] = []
let searchQuery: DocumentQuery<Space>
let resultQuery: DocumentQuery<Space>
let spaces: Space[] = []
$: update(search, sort)
$: updateSearchQuery(search)
$: update(sort, resultQuery)
async function update (search: string, sort: SortingQuery<Space>): Promise<void> {
const query = search.trim().length > 0 ? { name: { $like: '%' + search + '%' } } : {}
async function update (sort: SortingQuery<Space>, resultQuery: DocumentQuery<Space>): Promise<void> {
const options: FindOptions<Space> = {
sort
}
spaceQuery.query(
_class,
query,
{
...resultQuery
},
(res) => {
spaces = res.filter((p) => !p.private || p.members.includes(me))
},
@ -63,6 +81,10 @@
)
}
function updateSearchQuery (search: string): void {
searchQuery = search.length ? { $search: search } : {}
}
function showCreateDialog (ev: Event) {
showPopup(createItemDialog as AnyComponent, {}, 'middle')
}
@ -92,15 +114,31 @@
}
</script>
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label {label} /></span>
{#if withHeader}
<div class="ac-header full divide">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label {label} /></span>
</div>
{#if createItemDialog}
<Button label={createItemLabel} on:click={(ev) => showCreateDialog(ev)} />
{/if}
</div>
{#if createItemDialog}
<Button label={createItemLabel} on:click={(ev) => showCreateDialog(ev)} />
{/if}
</div>
<div class="ml-8 mr-8 mt-4"><SearchEdit bind:value={search} /></div>
<div class="ml-8 mr-8 mt-4 mb-4">
<SearchEdit
bind:value={search}
on:change={(ev) => {
updateSearchQuery(search)
update(sort, resultQuery)
}}
/>
</div>
{/if}
{#if withFilterButton}
<div class="ml-10 mt-4 mb-4">
<FilterButton {_class} bind:filters />
</div>
{/if}
<FilterBar {_class} query={searchQuery} bind:filters on:change={(e) => (resultQuery = e.detail)} />
<Scroller padding={'2.5rem'}>
<div class="flex-col">
{#each spaces as space (space._id)}
@ -137,9 +175,11 @@
</div>
</div>
{/each}
<div class="flex-center mt-10">
<Button size={'x-large'} kind={'primary'} label={createItemLabel} on:click={(ev) => showCreateDialog(ev)} />
</div>
{#if createItemDialog}
<div class="flex-center mt-10">
<Button size={'x-large'} kind={'primary'} label={createItemLabel} on:click={(ev) => showCreateDialog(ev)} />
</div>
{/if}
</div>
</Scroller>

View File

@ -26,7 +26,7 @@ import { doNavigate } from './utils'
function hasArchiveSpaces (spaces: Space[]): boolean {
return spaces.find((sp) => sp.archived) !== undefined
}
export { default as SpaceBrowser } from './components/SpaceBrowser.svelte'
export default async (): Promise<Resources> => ({
component: {
WorkbenchApp,