mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
Support for markup type (#936)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
53713d8c18
commit
666b4446b8
File diff suppressed because it is too large
Load Diff
@ -6,9 +6,9 @@
|
||||
"build": "cross-env NODE_ENV=production webpack --stats-error-details && echo 'done'",
|
||||
"analyze": "cross-env NODE_ENV=production webpack --json > stats.json",
|
||||
"show": "webpack-bundle-analyzer stats.json dist",
|
||||
"dev": "cross-env CLIENT_TYPE=dev webpack serve --content-base public",
|
||||
"dev-server": "cross-env CLIENT_TYPE=dev-server webpack serve --content-base public",
|
||||
"start": "cross-env NODE_ENV=production webpack serve --content-base public",
|
||||
"dev": "cross-env CLIENT_TYPE=dev webpack serve",
|
||||
"dev-server": "cross-env CLIENT_TYPE=dev-server webpack serve",
|
||||
"start": "cross-env NODE_ENV=production webpack serve",
|
||||
"preformat-svelte": "prettier -w src/**/*.svelte",
|
||||
"lint": "eslint --max-warnings=0 src",
|
||||
"lint:fix": "yarn preformat-svelte && eslint --fix src",
|
||||
@ -21,9 +21,9 @@
|
||||
"mini-css-extract-plugin": "^2.2.0",
|
||||
"dotenv-webpack": "^7.0.2",
|
||||
"ts-loader": "^9.2.5",
|
||||
"svelte-loader": "^3.1.0",
|
||||
"svelte-loader": "^3.1.2",
|
||||
"css-loader": "^5.2.1",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-dev-server": "^4.7.4",
|
||||
"style-loader": "^3.2.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass-loader": "^12.1.0",
|
||||
|
@ -19,6 +19,7 @@ const path = require('path')
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
const DefinePlugin = require('webpack').DefinePlugin
|
||||
const { resolve } = require('path')
|
||||
|
||||
const mode = process.env.NODE_ENV || 'development'
|
||||
const prod = mode === 'production'
|
||||
@ -29,7 +30,7 @@ module.exports = {
|
||||
entry: {
|
||||
bundle: [
|
||||
'@anticrm/theme/styles/global.scss',
|
||||
...(dev ? ['./src/main-dev.ts']: ['./src/main.ts'] )
|
||||
...(dev ? ['./src/main-dev.ts']: ['./src/main.ts'] ),
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
@ -59,10 +60,37 @@ module.exports = {
|
||||
loader: 'svelte-loader',
|
||||
options: {
|
||||
compilerOptions: {
|
||||
dev: !prod,
|
||||
dev: !prod
|
||||
},
|
||||
emitCss: true,
|
||||
preprocess: require('svelte-preprocess')({ postcss: true })
|
||||
hotReload: !prod,
|
||||
preprocess: require('svelte-preprocess')({ postcss: true }),
|
||||
hotOptions: {
|
||||
// Prevent preserving local component state
|
||||
preserveLocalState: true,
|
||||
|
||||
// If this string appears anywhere in your component's code, then local
|
||||
// state won't be preserved, even when noPreserveState is false
|
||||
noPreserveStateKey: '@!hmr',
|
||||
|
||||
// Prevent doing a full reload on next HMR update after fatal error
|
||||
noReload: true,
|
||||
|
||||
// Try to recover after runtime errors in component init
|
||||
optimistic: false,
|
||||
|
||||
// --- Advanced ---
|
||||
|
||||
// Prevent adding an HMR accept handler to components with
|
||||
// accessors option to true, or to components with named exports
|
||||
// (from <script context="module">). This have the effect of
|
||||
// recreating the consumer of those components, instead of the
|
||||
// component themselves, on HMR updates. This might be needed to
|
||||
// reflect changes to accessors / named exports in the parents,
|
||||
// depending on how you use them.
|
||||
acceptAccessors: true,
|
||||
acceptNamedExports: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -140,13 +168,24 @@ module.exports = {
|
||||
new Dotenv({path: prod ? '.env-prod' : '.env'}),
|
||||
new DefinePlugin({
|
||||
'process.env.CLIENT_TYPE': JSON.stringify(process.env.CLIENT_TYPE)
|
||||
})
|
||||
})
|
||||
],
|
||||
devtool: prod ? false : 'source-map',
|
||||
devtool: prod ? false : 'inline-source-map',
|
||||
devServer: {
|
||||
publicPath: '/',
|
||||
static: {
|
||||
directory: path.resolve(__dirname, "public"),
|
||||
publicPath: "/",
|
||||
serveIndex: true,
|
||||
watch: true,
|
||||
},
|
||||
historyApiFallback: {
|
||||
disableDotRule: true
|
||||
},
|
||||
hot: true,
|
||||
client: {
|
||||
logging: "info",
|
||||
overlay: false,
|
||||
progress: false,
|
||||
},
|
||||
proxy: devServer ? {
|
||||
'/account': {
|
||||
|
@ -17,7 +17,7 @@ import activity from '@anticrm/activity'
|
||||
import type { Backlink, Channel, Comment, Message } from '@anticrm/chunter'
|
||||
import type { Class, Doc, Domain, Ref } from '@anticrm/core'
|
||||
import { IndexKind } from '@anticrm/core'
|
||||
import { Builder, Index, Model, Prop, TypeString, UX } from '@anticrm/model'
|
||||
import { Builder, Index, Model, Prop, TypeMarkup, UX } from '@anticrm/model'
|
||||
import core, { TAttachedDoc, TDoc, TSpace } from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
@ -34,7 +34,7 @@ export class TChannel extends TSpace implements Channel {}
|
||||
|
||||
@Model(chunter.class.Message, core.class.Doc, DOMAIN_CHUNTER)
|
||||
export class TMessage extends TDoc implements Message {
|
||||
@Prop(TypeString(), 'Content' as IntlString)
|
||||
@Prop(TypeMarkup(), 'Content' as IntlString)
|
||||
@Index(IndexKind.FullText)
|
||||
content!: string
|
||||
}
|
||||
@ -42,7 +42,7 @@ export class TMessage extends TDoc implements Message {
|
||||
@Model(chunter.class.Comment, core.class.AttachedDoc, DOMAIN_COMMENT)
|
||||
@UX('Comment' as IntlString)
|
||||
export class TComment extends TAttachedDoc implements Comment {
|
||||
@Prop(TypeString(), 'Message' as IntlString)
|
||||
@Prop(TypeMarkup(), 'Message' as IntlString)
|
||||
@Index(IndexKind.FullText)
|
||||
message!: string
|
||||
}
|
||||
|
@ -49,13 +49,13 @@ export class TContact extends TDoc implements Contact {
|
||||
|
||||
avatar?: string
|
||||
|
||||
@Prop(Collection(contact.class.Channel), 'Contact Info' as IntlString)
|
||||
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
|
||||
channels?: number
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
|
||||
attachments?: number
|
||||
|
||||
@Prop(Collection(chunter.class.Comment), 'Comments' as IntlString)
|
||||
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
@Prop(TypeString(), 'Location' as IntlString)
|
||||
|
@ -48,7 +48,8 @@ export const ids = mergeIds(contactId, contact, {
|
||||
CreateOrganizations: '' as IntlString,
|
||||
SearchEmployee: '' as IntlString,
|
||||
SearchPerson: '' as IntlString,
|
||||
SearchOrganization: '' as IntlString
|
||||
SearchOrganization: '' as IntlString,
|
||||
ContactInfo: '' as IntlString
|
||||
},
|
||||
completion: {
|
||||
PersonQuery: '' as Resource<ObjectSearchFactory>,
|
||||
|
@ -106,6 +106,9 @@ export class TType extends TObj implements Type<any> {
|
||||
@Model(core.class.TypeString, core.class.Type)
|
||||
export class TTypeString extends TType {}
|
||||
|
||||
@Model(core.class.TypeMarkup, core.class.Type)
|
||||
export class TTypeMarkup extends TType {}
|
||||
|
||||
@Model(core.class.RefTo, core.class.Type)
|
||||
export class TRefTo extends TType implements RefTo<Doc> {
|
||||
to!: Ref<Class<Doc>>
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
TTypeBoolean,
|
||||
TTypeDate,
|
||||
TTypeString,
|
||||
TTypeMarkup,
|
||||
TTypeTimestamp,
|
||||
TVersion
|
||||
} from './core'
|
||||
@ -74,6 +75,7 @@ export function createModel (builder: Builder): void {
|
||||
TAttribute,
|
||||
TType,
|
||||
TTypeString,
|
||||
TTypeMarkup,
|
||||
TTypeBoolean,
|
||||
TTypeTimestamp,
|
||||
TRefTo,
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import { Doc, FindOptions, Lookup, Ref, Timestamp } from '@anticrm/core'
|
||||
import { Builder, Collection, Mixin, Model, Prop, TypeBoolean, TypeDate, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import { Builder, Collection, Mixin, Model, Prop, TypeBoolean, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import contact, { TPerson } from '@anticrm/model-contact'
|
||||
@ -31,7 +31,7 @@ import presentation from '@anticrm/model-presentation'
|
||||
@Model(recruit.class.Vacancy, task.class.SpaceWithStates)
|
||||
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
||||
export class TVacancy extends TSpaceWithStates implements Vacancy {
|
||||
@Prop(TypeString(), 'Full description' as IntlString)
|
||||
@Prop(TypeMarkup(), 'Full description' as IntlString)
|
||||
fullDescription?: string
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||
@ -71,19 +71,19 @@ export class TCandidate extends TPerson implements Candidate {
|
||||
}
|
||||
|
||||
@Model(recruit.class.Applicant, task.class.Task)
|
||||
@UX('Application' as IntlString, recruit.icon.Application, 'APP' as IntlString, 'number')
|
||||
@UX(recruit.string.Application, recruit.icon.Application, 'APP' as IntlString, 'number')
|
||||
export class TApplicant extends TTask implements Applicant {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(recruit.mixin.Candidate), 'Candidate' as IntlString)
|
||||
@Prop(TypeRef(recruit.mixin.Candidate), recruit.string.Candidate)
|
||||
declare attachedTo: Ref<Candidate>
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
|
||||
attachments?: number
|
||||
|
||||
@Prop(Collection(chunter.class.Comment), 'Comments' as IntlString)
|
||||
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), 'Assigned recruiter' as IntlString)
|
||||
@Prop(TypeRef(contact.class.Employee), recruit.string.AssignedRecruiter)
|
||||
declare assignee: Ref<Employee> | null
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,9 @@ export default mergeIds(recruitId, recruit, {
|
||||
RecruitApplication: '' as IntlString,
|
||||
Vacancies: '' as IntlString,
|
||||
CandidatePools: '' as IntlString,
|
||||
SearchApplication: '' as IntlString
|
||||
SearchApplication: '' as IntlString,
|
||||
Application: '' as IntlString,
|
||||
AssignedRecruiter: '' as IntlString
|
||||
},
|
||||
validator: {
|
||||
ApplicantValidator: '' as Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
Model,
|
||||
Prop, TypeBoolean,
|
||||
TypeDate,
|
||||
TypeMarkup,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX
|
||||
@ -131,7 +132,7 @@ export class TIssue extends TTask implements Issue {
|
||||
@Prop(TypeString(), task.string.IssueName)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeString(), task.string.TaskDescription)
|
||||
@Prop(TypeMarkup(), task.string.TaskDescription)
|
||||
description!: string
|
||||
|
||||
@Prop(Collection(chunter.class.Comment), task.string.TaskComments)
|
||||
|
@ -118,6 +118,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: view.component.StringPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeMarkup, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: view.component.HTMLPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeBoolean, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: view.component.BooleanPresenter
|
||||
})
|
||||
|
@ -31,6 +31,7 @@ export default mergeIds(viewId, view, {
|
||||
component: {
|
||||
StringEditor: '' as AnyComponent,
|
||||
StringPresenter: '' as AnyComponent,
|
||||
HTMLPresenter: '' as AnyComponent,
|
||||
BooleanPresenter: '' as AnyComponent,
|
||||
BooleanEditor: '' as AnyComponent,
|
||||
TimestampPresenter: '' as AnyComponent,
|
||||
|
@ -44,6 +44,7 @@ export default plugin(coreId, {
|
||||
Space: '' as Ref<Class<Space>>,
|
||||
Account: '' as Ref<Class<Account>>,
|
||||
TypeString: '' as Ref<Class<Type<string>>>,
|
||||
TypeMarkup: '' as Ref<Class<Type<string>>>,
|
||||
TypeBoolean: '' as Ref<Class<Type<boolean>>>,
|
||||
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,
|
||||
TypeDate: '' as Ref<Class<Type<Timestamp | Date>>>,
|
||||
|
@ -328,6 +328,13 @@ export function TypeString (): Type<string> {
|
||||
return { _class: core.class.TypeString, label: 'TypeString' as IntlString }
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeMarkup (): Type<string> {
|
||||
return { _class: core.class.TypeMarkup, label: 'TypeMarkup' as IntlString }
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -1,6 +1,10 @@
|
||||
{
|
||||
"string": {
|
||||
"Suggested": "SUGGESTED",
|
||||
"NoItems": "No items"
|
||||
"NoItems": "No items",
|
||||
"EditorPlaceholder": "Start typing...",
|
||||
"Edit": "Edit",
|
||||
"Cancel": "Cancel",
|
||||
"Save": "Save"
|
||||
}
|
||||
}
|
||||
|
90
packages/text-editor/src/components/StyledTextBox.svelte
Normal file
90
packages/text-editor/src/components/StyledTextBox.svelte
Normal file
@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { MessageViewer } from '@anticrm/presentation'
|
||||
import { ActionIcon, IconCheck, IconClose, IconEdit } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import textEditorPlugin from '../plugin'
|
||||
import StyledTextEditor from './StyledTextEditor.svelte'
|
||||
|
||||
export let content: string
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
let rawValue: string
|
||||
|
||||
const Mode = {
|
||||
View: 1,
|
||||
Edit: 2
|
||||
}
|
||||
let mode = Mode.View
|
||||
|
||||
let textEditor: StyledTextEditor
|
||||
|
||||
export function submit (): void {
|
||||
textEditor.submit()
|
||||
}
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="antiComponent styled-box">
|
||||
{#if mode !== Mode.View}
|
||||
<StyledTextEditor
|
||||
{placeholder}
|
||||
bind:content={rawValue}
|
||||
bind:this={textEditor}
|
||||
on:value={(evt) => {
|
||||
rawValue = evt.detail
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-reverse flex-grow">
|
||||
<div class="ml-2">
|
||||
<!-- disabled={rawValue.trim().length === 0} -->
|
||||
<ActionIcon
|
||||
icon={IconCheck}
|
||||
size={'medium'}
|
||||
direction={'bottom'}
|
||||
label={textEditorPlugin.string.Save}
|
||||
action={() => {
|
||||
dispatch('value', rawValue)
|
||||
content = rawValue
|
||||
mode = Mode.View
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ActionIcon
|
||||
size={'medium'}
|
||||
icon={IconClose}
|
||||
direction={'top'}
|
||||
label={textEditorPlugin.string.Cancel}
|
||||
action={() => {
|
||||
mode = Mode.View
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</StyledTextEditor>
|
||||
{:else}
|
||||
<div class="text">
|
||||
<MessageViewer message={content} />
|
||||
</div>
|
||||
<div class="flex flex-reverse">
|
||||
<ActionIcon
|
||||
size={'medium'}
|
||||
icon={IconEdit}
|
||||
direction={'top'}
|
||||
label={textEditorPlugin.string.Edit}
|
||||
action={() => {
|
||||
rawValue = content ?? ''
|
||||
mode = Mode.Edit
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.styled-box {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
line-height: 150%;
|
||||
}
|
||||
</style>
|
@ -13,16 +13,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
|
||||
import { ScrollBox } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Emoji from './icons/Emoji.svelte'
|
||||
import GIF from './icons/GIF.svelte'
|
||||
import TextStyle from './icons/TextStyle.svelte'
|
||||
import TextEditor from './TextEditor.svelte'
|
||||
import textEditorPlugin from '../plugin'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
|
||||
let textEditor: TextEditor
|
||||
|
||||
@ -37,6 +41,7 @@
|
||||
<ScrollBox bothScroll stretch>
|
||||
<TextEditor
|
||||
bind:content
|
||||
{placeholder}
|
||||
bind:this={textEditor}
|
||||
on:value
|
||||
on:content={(ev) => {
|
||||
@ -44,6 +49,7 @@
|
||||
content = ''
|
||||
textEditor.clear()
|
||||
}}
|
||||
on:blur
|
||||
supportSubmit={false}
|
||||
/>
|
||||
</ScrollBox>
|
||||
|
@ -13,8 +13,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { IntlString, translate } from '@anticrm/platform'
|
||||
|
||||
import { AnyExtension, Editor, Extension, HTMLContent } from '@tiptap/core'
|
||||
import Highlight from '@tiptap/extension-highlight'
|
||||
import Link from '@tiptap/extension-link'
|
||||
@ -22,15 +23,22 @@
|
||||
import Placeholder from '@tiptap/extension-placeholder'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import textEditorPlugin from '../plugin'
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: string = 'Type something...'
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
export let extensions: AnyExtension[] = []
|
||||
export let supportSubmit = true
|
||||
|
||||
let element: HTMLElement
|
||||
let editor: Editor
|
||||
|
||||
let placeHolderStr: string = ''
|
||||
|
||||
$: ph = translate(placeholder, {}).then((r) => {
|
||||
placeHolderStr = r
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export function submit (): void {
|
||||
@ -70,29 +78,31 @@
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
editor = new Editor({
|
||||
element,
|
||||
content: content,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Highlight,
|
||||
Link,
|
||||
...(supportSubmit ? [Handle] : []), // order important
|
||||
// Typography, // we need to disable 1/2 -> ½ rule (https://github.com/hcengineering/anticrm/issues/345)
|
||||
Placeholder.configure({ placeholder: placeholder }),
|
||||
...extensions
|
||||
],
|
||||
onTransaction: () => {
|
||||
// force re-render so `editor.isActive` works as expected
|
||||
editor = editor
|
||||
},
|
||||
onBlur: () => {
|
||||
dispatch('blur')
|
||||
},
|
||||
onUpdate: () => {
|
||||
content = editor.getHTML()
|
||||
dispatch('value', content)
|
||||
}
|
||||
ph.then(() => {
|
||||
editor = new Editor({
|
||||
element,
|
||||
content: content,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Highlight,
|
||||
Link,
|
||||
...(supportSubmit ? [Handle] : []), // order important
|
||||
// Typography, // we need to disable 1/2 -> ½ rule (https://github.com/hcengineering/anticrm/issues/345)
|
||||
Placeholder.configure({ placeholder: placeHolderStr }),
|
||||
...extensions
|
||||
],
|
||||
onTransaction: () => {
|
||||
// force re-render so `editor.isActive` works as expected
|
||||
editor = editor
|
||||
},
|
||||
onBlur: () => {
|
||||
dispatch('blur', editor.getHTML())
|
||||
},
|
||||
onUpdate: () => {
|
||||
content = editor.getHTML()
|
||||
dispatch('value', content)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -103,35 +113,39 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<div style="width: 100%;" bind:this={element}/>
|
||||
<div style="width: 100%;" bind:this={element} />
|
||||
|
||||
<style lang="scss" global>
|
||||
.ProseMirror {
|
||||
overflow-y: auto;
|
||||
max-height: 5.5rem;
|
||||
outline: none;
|
||||
line-height: 150%;
|
||||
p:not(:last-child) {
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
||||
.ProseMirror {
|
||||
overflow-y: auto;
|
||||
max-height: 5.5rem;
|
||||
outline: none;
|
||||
line-height: 150%;
|
||||
p:not(:last-child) {
|
||||
margin-block-end: 1em;
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
/* Placeholder (at the top) */
|
||||
p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: var(--theme-content-trans-color);
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--theme-bg-accent-hover);
|
||||
}
|
||||
&::-webkit-scrollbar-corner {
|
||||
background-color: var(--theme-bg-accent-hover);
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
/* Placeholder (at the top) */
|
||||
p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: var(--theme-content-trans-color);
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb { background-color: var(--theme-bg-accent-hover); }
|
||||
&::-webkit-scrollbar-corner { background-color: var(--theme-bg-accent-hover); }
|
||||
&::-webkit-scrollbar-track { margin: 0; }
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
@ -23,9 +23,10 @@ export * from './types'
|
||||
export { default as ReferenceInput } from './components/ReferenceInput.svelte'
|
||||
export { default as TextEditor } from './components/TextEditor.svelte'
|
||||
export { default as StyledTextEditor } from './components/StyledTextEditor.svelte'
|
||||
export { default as StyledTextBox } from './components/StyledTextBox.svelte'
|
||||
|
||||
addStringsLoader(textEditorId, async (lang: string) => {
|
||||
return await import(`../lang/${lang}.json`)
|
||||
})
|
||||
|
||||
export { default } from './plugin'
|
||||
export { default } from './plugin'
|
||||
|
@ -33,6 +33,10 @@ export default plugin(textEditorId, {
|
||||
Attach: '' as IntlString,
|
||||
TextStyle: '' as IntlString,
|
||||
Emoji: '' as IntlString,
|
||||
GIF: '' as IntlString
|
||||
GIF: '' as IntlString,
|
||||
EditorPlaceholder: '' as IntlString,
|
||||
Edit: '' as IntlString,
|
||||
Cancel: '' as IntlString,
|
||||
Save: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
9
packages/ui/src/components/icons/Check.svelte
Normal file
9
packages/ui/src/components/icons/Check.svelte
Normal file
@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" viewBox="0 0 16 16" fill='none' xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 8.3324L6.65721 13L14 3" stroke={fill} stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
@ -87,6 +87,7 @@ export { default as IconDelete } from './components/icons/Delete.svelte'
|
||||
export { default as IconEdit } from './components/icons/Edit.svelte'
|
||||
export { default as IconInfo } from './components/icons/Info.svelte'
|
||||
export { default as IconBlueCheck } from './components/icons/BlueCheck.svelte'
|
||||
export { default as IconCheck } from './components/icons/Check.svelte'
|
||||
export { default as IconArrowLeft } from './components/icons/ArrowLeft.svelte'
|
||||
|
||||
export { default as PanelInstance } from './components/PanelInstance.svelte'
|
||||
|
@ -18,7 +18,6 @@
|
||||
import { CommentInput } from '@anticrm/chunter-resources'
|
||||
import { Doc, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { ReferenceInput } from '@anticrm/text-editor'
|
||||
import { Grid, IconActivity, ScrollBox } from '@anticrm/ui'
|
||||
import { ActivityKey, activityKey, DisplayTx, newActivity } from '../activity'
|
||||
import TxView from './TxView.svelte'
|
||||
@ -51,7 +50,7 @@
|
||||
</script>
|
||||
|
||||
{#if fullSize || transparent}
|
||||
{#if !transparent}
|
||||
{#if transparent !== undefined && !transparent}
|
||||
<div class="flex-row-center header">
|
||||
<div class="flex-center icon"><IconActivity size={'small'} /></div>
|
||||
<div class="fs-title">Activity</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
import type { TxViewlet } from '@anticrm/activity'
|
||||
import activity from '@anticrm/activity'
|
||||
import contact, { EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { Doc, Ref } from '@anticrm/core'
|
||||
import core, { AnyAttribute, Doc, Ref } from '@anticrm/core'
|
||||
import { Asset, getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import {
|
||||
@ -106,6 +106,11 @@
|
||||
edit = false
|
||||
props = { ...props, edit }
|
||||
}
|
||||
function isMessageType (attr?: AnyAttribute): boolean {
|
||||
return attr?.type._class === core.class.TypeMarkup
|
||||
}
|
||||
|
||||
$: hasMessageType = model.find(m => isMessageType(m.attribute))
|
||||
</script>
|
||||
{#if (viewlet !== undefined && !((viewlet?.hideOnRemove ?? false) && tx.removed)) || model.length > 0}
|
||||
<div class="flex-between msgactivity-container">
|
||||
@ -153,14 +158,25 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if viewlet === undefined && model.length > 0 && tx.updateTx}
|
||||
{#each model as m}
|
||||
{#if viewlet === undefined && model.length > 0 && tx.updateTx}
|
||||
{#each model as m, i}
|
||||
{#await getValue(client, m, tx.updateTx.operations) then value}
|
||||
{#if value === null}
|
||||
<span>unset <Label label={m.label} /></span>
|
||||
{:else}
|
||||
<span>changed <Label label={m.label} /> to</span>
|
||||
<div class="strong"><svelte:component this={m.presenter} {value} /></div>
|
||||
<span class:flex-grow={hasMessageType}>changed <Label label={m.label} /> to</span>
|
||||
{#if hasMessageType}
|
||||
<div class="time"><TimeSince value={tx.tx.modifiedOn} /></div>
|
||||
{/if}
|
||||
{#if isMessageType(m.attribute)}
|
||||
<div class="strong message emphasized">
|
||||
<svelte:component this={m.presenter} {value} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="strong">
|
||||
<svelte:component this={m.presenter} {value} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/await}
|
||||
{/each}
|
||||
@ -171,7 +187,15 @@
|
||||
<span>unset <Label label={m.label} /></span>
|
||||
{:else}
|
||||
<span>changed <Label label={m.label} /> to</span>
|
||||
<div class="strong"><svelte:component this={m.presenter} {value} /></div>
|
||||
{#if isMessageType(m.attribute)}
|
||||
<div class="strong message emphasized">
|
||||
<svelte:component this={m.presenter} {value} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="strong">
|
||||
<svelte:component this={m.presenter} {value} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/await}
|
||||
{/each}
|
||||
@ -183,7 +207,9 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="time"><TimeSince value={tx.tx.modifiedOn} /></div>
|
||||
{#if !hasMessageType}
|
||||
<div class="time"><TimeSince value={tx.tx.modifiedOn} /></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if viewlet && viewlet.component && viewlet.display !== 'inline'}
|
||||
@ -282,4 +308,8 @@
|
||||
border-radius: .75rem;
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.message {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,6 +14,7 @@
|
||||
"EditUpdate": "Save...",
|
||||
"EditCancel": "Cancel",
|
||||
"Comments" : "Comments",
|
||||
"MentionedIn": "mentioned this "
|
||||
"MentionedIn": "mentioned this ",
|
||||
"ContactInfo": "Contact Info"
|
||||
}
|
||||
}
|
@ -4,11 +4,12 @@
|
||||
"Vacancies": "Vacancies",
|
||||
"CandidatePools": "Candidate’s pool",
|
||||
"Candidates": "Candidates",
|
||||
"VacancyName": "Vacancy Title *",
|
||||
"VacancyName": "Vacancy Name *",
|
||||
"VacancyDescription": "Vacancy Description",
|
||||
"CreateVacancy": "Create Vacancy",
|
||||
"MakePrivate": "Make Private",
|
||||
"Vacancy": "Vacancy",
|
||||
"VacancyPlaceholder": "Please type vacancy name",
|
||||
"CandidatesName": "Pool name *",
|
||||
"MakePrivateDescription": "Only members can see it",
|
||||
"CreateAnApplication": "Create an application",
|
||||
@ -31,7 +32,9 @@
|
||||
"WorkLocationPreferences": "Work location preferences",
|
||||
"Onsite": "Onsite",
|
||||
"Remote": "Remote",
|
||||
"SearchApplication": "Search for application..."
|
||||
"SearchApplication": "Search for application...",
|
||||
"Application": "Application",
|
||||
"AssignedRecruiter": "Assigned recruiter"
|
||||
},
|
||||
"status": {
|
||||
"CandidateRequired": "Please select candidate",
|
||||
|
@ -15,15 +15,15 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { IconClose, Label, EditBox, ToggleWithLabel, Grid, Icon, Component } from '@anticrm/ui'
|
||||
import { TextEditor } from '@anticrm/text-editor'
|
||||
import { AttributesBar, getClient, createQuery } from '@anticrm/presentation'
|
||||
import { Vacancy } from '@anticrm/recruit'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import activity from '@anticrm/activity'
|
||||
import { Attachments } from '@anticrm/attachment-resources'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { AttributesBar, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Vacancy } from '@anticrm/recruit'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { Component, EditBox, Grid, Icon, IconClose, Label, ToggleWithLabel } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let _id: Ref<Vacancy>
|
||||
@ -36,11 +36,17 @@
|
||||
|
||||
const query = createQuery()
|
||||
const clazz = client.getHierarchy().getClass(recruit.class.Vacancy)
|
||||
$: query.query(recruit.class.Vacancy, { _id }, result => { object = result[0] })
|
||||
|
||||
async function updateObject (_id: Ref<Vacancy>): Promise<void> {
|
||||
await query.query(recruit.class.Vacancy, { _id }, result => {
|
||||
object = result[0]
|
||||
})
|
||||
}
|
||||
|
||||
$: updateObject(_id)
|
||||
|
||||
const tabs: IntlString[] = ['General' as IntlString, 'Members' as IntlString, 'Activity' as IntlString]
|
||||
let selected = 0
|
||||
let textEditor: TextEditor
|
||||
|
||||
function onChange (key:string, value: any): void {
|
||||
client.updateDoc(object._class, object.space, object._id, { [key]: value })
|
||||
@ -81,13 +87,20 @@
|
||||
<div class="flex-col box">
|
||||
{#if selected === 0}
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox label={recruit.string.VacancyName} bind:value={object.name} placeholder="Software Engineer" maxWidth="39rem" focus on:change={() => {onChange('name', object.name)}}/>
|
||||
<EditBox label={recruit.string.Description} bind:value={object.description} placeholder='Description' maxWidth="39rem" focus on:change={() => {onChange('description', object.description)}}/>
|
||||
<EditBox label={recruit.string.VacancyName} bind:value={object.name} placeholder={recruit.string.VacancyPlaceholder} maxWidth="39rem" focus on:change={() => {
|
||||
if (object.name.trim().length > 0) {
|
||||
onChange('name', object.name)
|
||||
} else {
|
||||
// Revert previos object.name
|
||||
updateObject(_id)
|
||||
}
|
||||
}}/>
|
||||
<EditBox label={recruit.string.Description} bind:value={object.description} placeholder={recruit.string.VacancyDescription} maxWidth="39rem" focus on:change={() => { onChange('description', object.description) }}/>
|
||||
</Grid>
|
||||
<div class="mt-10">
|
||||
<span class="title">Description</span>
|
||||
<span class="title">Details</span>
|
||||
<div class="description-container">
|
||||
<TextEditor bind:this={textEditor} bind:content={object.fullDescription} on:blur={textEditor.submit} on:content={() => {onChange('fullDescription', object.fullDescription)}} />
|
||||
<StyledTextBox bind:content={object.fullDescription} on:value={(evt) => { onChange('fullDescription', evt.detail) }} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-14">
|
||||
@ -96,7 +109,7 @@
|
||||
{:else if selected === 1}
|
||||
<ToggleWithLabel label={recruit.string.ThisVacancyIsPrivate} description={recruit.string.MakePrivateDescription}/>
|
||||
{:else if selected === 2}
|
||||
<Component is={activity.component.Activity} props={{object, transparent: true}} />
|
||||
<Component is={activity.component.Activity} props={{ object, transparent: true }} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@ -205,7 +218,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
overflow-y: auto;
|
||||
height: 100px;
|
||||
height: 15rem;
|
||||
padding: 0px 16px;
|
||||
background-color: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
|
@ -27,6 +27,7 @@ export default mergeIds(recruitId, recruit, {
|
||||
string: {
|
||||
CreateVacancy: '' as IntlString,
|
||||
VacancyName: '' as IntlString,
|
||||
VacancyPlaceholder: '' as IntlString,
|
||||
VacancyDescription: '' as IntlString,
|
||||
MakePrivate: '' as IntlString,
|
||||
MakePrivateDescription: '' as IntlString,
|
||||
|
@ -17,6 +17,7 @@
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import plugin from '../plugin'
|
||||
@ -54,13 +55,24 @@
|
||||
focus
|
||||
on:change={() => change('name', object.name)}
|
||||
/>
|
||||
<EditBox
|
||||
label={plugin.string.TaskDescription}
|
||||
bind:value={object.description}
|
||||
icon={task.icon.Task}
|
||||
placeholder={plugin.string.TaskDescriptionPlaceholder}
|
||||
maxWidth="39rem"
|
||||
on:change={() => change('description', object.description)}
|
||||
/>
|
||||
|
||||
<div class='description'>
|
||||
<StyledTextBox
|
||||
bind:content={object.description}
|
||||
placeholder={plugin.string.TaskDescriptionPlaceholder}
|
||||
on:blur={() => change('description', object.description)}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.description {
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
height: 12rem;
|
||||
border-radius: .25rem;
|
||||
background-color: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -50,7 +50,6 @@
|
||||
if (item.dueTo !== dueTo) {
|
||||
ops.dueTo = (dueTo?.getTime() ?? null) as unknown as Timestamp
|
||||
}
|
||||
console.log('AHTUNG', ops)
|
||||
|
||||
if (Object.keys(ops).length === 0) {
|
||||
return
|
||||
|
@ -115,7 +115,7 @@
|
||||
}}>
|
||||
<div class="flex flex-reverse flex-grow">
|
||||
<div class="ml-2">
|
||||
<Button disabled={newTemplate.title.trim().length == 0 } primary label={templatesPlugin.string.SaveTemplate} on:click={saveNewTemplate} />
|
||||
<Button disabled={newTemplate.title.trim().length === 0 } primary label={templatesPlugin.string.SaveTemplate} on:click={saveNewTemplate} />
|
||||
</div>
|
||||
<Button
|
||||
label={templatesPlugin.string.Cancel}
|
||||
|
23
plugins/view-resources/src/components/HTMLPresenter.svelte
Normal file
23
plugins/view-resources/src/components/HTMLPresenter.svelte
Normal file
@ -0,0 +1,23 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { MessageViewer } from '@anticrm/presentation'
|
||||
|
||||
export let value: string
|
||||
</script>
|
||||
|
||||
<MessageViewer message={value}/>
|
@ -31,6 +31,7 @@ import MoveView from './components/Move.svelte'
|
||||
import EditDoc from './components/EditDoc.svelte'
|
||||
import RolePresenter from './components/RolePresenter.svelte'
|
||||
import ObjectPresenter from './components/ObjectPresenter.svelte'
|
||||
import HTMLPresenter from './components/HTMLPresenter.svelte'
|
||||
|
||||
export { default as ContextMenu } from './components/Menu.svelte'
|
||||
export { buildModel, getActions, getObjectPresenter, LoadingProps } from './utils'
|
||||
@ -72,6 +73,7 @@ export default async (): Promise<Resources> => ({
|
||||
DatePresenter,
|
||||
RolePresenter,
|
||||
ObjectPresenter,
|
||||
EditDoc
|
||||
EditDoc,
|
||||
HTMLPresenter
|
||||
}
|
||||
})
|
||||
|
@ -101,7 +101,8 @@ async function getAttributePresenter (
|
||||
_class: attrClass,
|
||||
label: preserveKey.label ?? attribute.label,
|
||||
presenter,
|
||||
icon: presenterMixin.icon
|
||||
icon: presenterMixin.icon,
|
||||
attribute
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Class, Client, Doc, DocumentQuery, FindOptions, Mixin, Obj, Ref, Space, TxOperations, UXObject } from '@anticrm/core'
|
||||
import type { AnyAttribute, Class, Client, Doc, DocumentQuery, FindOptions, Mixin, Obj, Ref, Space, TxOperations, UXObject } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin, Resource, Status } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
|
||||
@ -116,6 +116,8 @@ export interface AttributeModel {
|
||||
sortingKey: string
|
||||
// Extra icon if applicable
|
||||
icon?: Asset
|
||||
|
||||
attribute?: AnyAttribute
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,18 +14,18 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { IntlString, Asset } from '@anticrm/platform'
|
||||
import { Icon, Label } from '@anticrm/ui'
|
||||
import type { Asset } from '@anticrm/platform'
|
||||
import { Icon } from '@anticrm/ui'
|
||||
|
||||
export let icon: Asset | undefined
|
||||
export let label: IntlString
|
||||
export let description: IntlString | undefined
|
||||
export let label: string
|
||||
export let description: string | undefined
|
||||
</script>
|
||||
|
||||
<div class="ac-header__wrap-description">
|
||||
<div class="ac-header__wrap-title">
|
||||
{#if icon }<div class="ac-header__icon"><Icon {icon} size={'small'}/></div>{/if}
|
||||
<span class="ac-header__title"><Label {label}/></span>
|
||||
<span class="ac-header__title">{label}</span>
|
||||
</div>
|
||||
{#if description }<span class="ac-header__description">{description}</span>{/if}
|
||||
</div>
|
||||
|
@ -15,9 +15,9 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Ref, Class, Doc, Space, WithLookup } from '@anticrm/core'
|
||||
import type { Viewlet } from '@anticrm/view'
|
||||
import type { Class, Doc, Ref, Space, WithLookup } from '@anticrm/core'
|
||||
import { Component } from '@anticrm/ui'
|
||||
import type { Viewlet } from '@anticrm/view'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
@ -30,7 +30,7 @@
|
||||
<Component is={viewlet.$lookup?.descriptor?.component} props={ {
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
options: viewlet.options,
|
||||
config: viewlet.config,
|
||||
search
|
||||
} } />
|
||||
|
@ -19,7 +19,7 @@
|
||||
import core from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { EditBox, Grid, Icon, IconClose, Label, ToggleWithLabel } from '@anticrm/ui'
|
||||
import { EditBox, Grid, Icon, IconClose, Label } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let _id: Ref<Space>
|
||||
@ -42,7 +42,13 @@
|
||||
let selected = 0
|
||||
|
||||
function onNameChange (ev: Event) {
|
||||
client.updateDoc(spaceClass, space.space, space._id, { name: (ev.target as HTMLInputElement).value })
|
||||
const value = (ev.target as HTMLInputElement).value
|
||||
if (value.trim().length > 0) {
|
||||
client.updateDoc(spaceClass, space.space, space._id, { name: value })
|
||||
} else {
|
||||
// Just refresh value
|
||||
query.query(core.class.Space, { _id }, result => { space = result[0] })
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -71,9 +77,9 @@
|
||||
{#if selected === 0}
|
||||
{#if space}
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox label={clazz.label} icon={clazz.icon} bind:value={space.name} placeholder="Software Engineer" maxWidth="39rem" focus on:change={onNameChange}/>
|
||||
<EditBox label={clazz.label} icon={clazz.icon} bind:value={space.name} placeholder={clazz.label} maxWidth="39rem" focus on:change={onNameChange}/>
|
||||
<!-- <AttributeBarEditor maxWidth="39rem" object={space} key="name"/> -->
|
||||
<ToggleWithLabel label={'MakePrivate'} description={'MakePrivateDescription'}/>
|
||||
<!-- <ToggleWithLabel label={workbench.string.MakePrivate} description={workbench.string.MakePrivateDescription}/> -->
|
||||
</Grid>
|
||||
{/if}
|
||||
{:else}
|
||||
|
@ -17,12 +17,12 @@
|
||||
import core from '@anticrm/core'
|
||||
import { getResource, IntlString } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Action, IconAdd, IconEdit, showPopup } from '@anticrm/ui'
|
||||
import { Action, IconAdd, IconEdit, showPanel, showPopup } from '@anticrm/ui'
|
||||
import { getActions as getContributedActions } from '@anticrm/view-resources'
|
||||
import type { SpacesNavModel } from '@anticrm/workbench'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import plugin from '../../plugin'
|
||||
import { classIcon } from '../../utils'
|
||||
import SpacePanel from './SpacePanel.svelte'
|
||||
import TreeItem from './TreeItem.svelte'
|
||||
import TreeNode from './TreeNode.svelte'
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
label: 'Open' as IntlString,
|
||||
icon: IconEdit,
|
||||
action: async (_id: Ref<Doc>): Promise<void> => {
|
||||
showPopup(model.component ?? SpacePanel, { _id, spaceClass: model.spaceClass }, 'right')
|
||||
showPanel(model.component ?? plugin.component.SpacePanel, _id, model.spaceClass, 'right')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import Archive from './components/Archive.svelte'
|
||||
import { Space } from '@anticrm/core'
|
||||
import SpacePanel from './components/navigator/SpacePanel.svelte'
|
||||
|
||||
function hasArchiveSpaces (spaces: Space[]): boolean {
|
||||
return spaces.find(sp => sp.archived) !== undefined
|
||||
@ -27,7 +28,8 @@ export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
WorkbenchApp,
|
||||
ApplicationPresenter,
|
||||
Archive
|
||||
Archive,
|
||||
SpacePanel
|
||||
},
|
||||
function: {
|
||||
HasArchiveSpaces: hasArchiveSpaces
|
||||
|
@ -1,14 +1,14 @@
|
||||
//
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
@ -17,6 +17,7 @@ import { mergeIds } from '@anticrm/platform'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
|
||||
import workbench, { workbenchId } from '@anticrm/workbench'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
|
||||
export default mergeIds(workbenchId, workbench, {
|
||||
string: {
|
||||
@ -27,5 +28,8 @@ export default mergeIds(workbenchId, workbench, {
|
||||
HideMenu: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
Archived: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
SpacePanel: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -153,7 +153,7 @@ export class FullTextIndex implements WithFind {
|
||||
const allAttributes = this.hierarchy.getAllAttributes(clazz)
|
||||
const result: AnyAttribute[] = []
|
||||
for (const [, attr] of allAttributes) {
|
||||
if (attr.type._class === core.class.TypeString) {
|
||||
if (attr.type._class === core.class.TypeString || attr.type._class === core.class.TypeMarkup) {
|
||||
result.push(attr)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user