Skip to content

Commit 989bcb8

Browse files
committed
Add UI package and enhance comment editor integration
1 parent 85cabac commit 989bcb8

11 files changed

Lines changed: 1087 additions & 37 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ application-local.properties
7070
/packages/*/node_modules/
7171
node_modules
7272
/workplace/
73-
/src/main/resources/static/
73+
/src/main/resources/static/
74+
/src/main/resources/console/

packages/comment-widget/src/base-form.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export class BaseForm extends LitElement {
191191
override render() {
192192
return html`
193193
<form class="form w-full flex flex-col gap-4" @submit="${this.onSubmit}">
194-
<comment-editor ${ref(this.editorRef)}></comment-editor>
194+
<comment-editor ${ref(this.editorRef)} .placeholder=${this.configMapData?.editor?.placeholder}></comment-editor>
195195
${
196196
!this.currentUser && this.allowAnonymousComments
197197
? html`<div class="form-inputs grid grid-cols-1 md:grid-cols-4 gap-2 items-center">

packages/comment-widget/src/comment-editor.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import { repeat } from 'lit/directives/repeat.js';
66
import './emoji-button';
77
import contentStyles from './styles/content.css?inline';
88
import './comment-editor-skeleton';
9-
import { consume } from '@lit/context';
10-
import { configMapDataContext } from './context';
9+
import { property } from 'lit/decorators.js';
1110
import baseStyles from './styles/base';
12-
import type { ConfigMapData } from './types';
1311

1412
interface ActionItem {
1513
name?: string;
@@ -75,16 +73,18 @@ const actionItems: ActionItem[] = [
7573
];
7674

7775
export class CommentEditor extends LitElement {
76+
@property({ type: String })
77+
placeholder: string | undefined;
78+
79+
@property({ type: Boolean, attribute: 'keep-alive' })
80+
keepAlive = false;
81+
7882
@state()
7983
editor: Editor | undefined;
8084

8185
@state()
8286
loading = true;
8387

84-
@consume({ context: configMapDataContext })
85-
@state()
86-
configMapData: ConfigMapData | undefined;
87-
8888
protected override firstUpdated(_changedProperties: PropertyValues): void {
8989
super.firstUpdated(_changedProperties);
9090
this.createEditor();
@@ -113,8 +113,7 @@ export class CommentEditor extends LitElement {
113113
}),
114114

115115
Placeholder.configure({
116-
placeholder:
117-
this.configMapData?.editor?.placeholder || msg('Write a comment'),
116+
placeholder: this.placeholder || msg('Write a comment'),
118117
}),
119118

120119
CodeBlockShiki.configure({
@@ -133,10 +132,25 @@ export class CommentEditor extends LitElement {
133132
this.requestUpdate();
134133
},
135134
});
135+
136+
this.editor.on('update', () => {
137+
this.dispatchEvent(
138+
new CustomEvent('update', {
139+
detail: {
140+
content: this.editor?.getHTML(),
141+
characterCount: this.editor?.storage.characterCount.characters(),
142+
},
143+
})
144+
);
145+
});
136146
}
137147

138148
override disconnectedCallback(): void {
139-
this.editor?.destroy();
149+
if (!this.keepAlive) {
150+
this.editor?.destroy();
151+
this.editor = undefined;
152+
}
153+
super.disconnectedCallback();
140154
}
141155

142156
setFocus() {

packages/comment-widget/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BaseCommentItem } from './base-comment-item';
22
import { BaseForm } from './base-form';
3+
import { CommentEditor } from './comment-editor';
34
import { CommentItem } from './comment-item';
45
import { CommentList } from './comment-list';
56
import { CommentPagination } from './comment-pagination';
@@ -25,4 +26,5 @@ export {
2526
UserAvatar,
2627
CommentList,
2728
LitToast,
29+
CommentEditor,
2830
};

packages/ui/env.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="@rsbuild/core/types" />
2+
/// <reference types="unplugin-icons/types/vue" />

packages/ui/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"type": "module",
3+
"scripts": {
4+
"build": "rsbuild build",
5+
"dev": "rsbuild build --watch --env-mode=development",
6+
"type-check": "vue-tsc --build"
7+
},
8+
"dependencies": {
9+
"@halo-dev/comment-widget": "workspace:*",
10+
"@halo-dev/components": "^2.21.0",
11+
"@halo-dev/console-shared": "link:/Users/ryanwang/Workspace/github/ruibaby/halo-next/ui/packages/shared",
12+
"vue": "^3.5.17"
13+
},
14+
"devDependencies": {
15+
"@halo-dev/ui-plugin-bundler-kit": "^2.21.2",
16+
"@rsbuild/core": "^1.4.3",
17+
"@rsbuild/plugin-vue": "^1.1.1",
18+
"@types/node": "^20.19.1",
19+
"@vue/tsconfig": "^0.7.0",
20+
"typescript": "~5.8.3",
21+
"vue-tsc": "^2.2.10"
22+
}
23+
}

packages/ui/rsbuild.config.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { rsbuildConfig } from '@halo-dev/ui-plugin-bundler-kit';
2+
import { pluginVue } from '@rsbuild/plugin-vue';
3+
4+
const MANIFEST_PATH = '../../src/main/resources/plugin.yaml';
5+
const OUT_DIR_PROD = '../../src/main/resources/console';
6+
const OUT_DIR_DEV = '../../build/resources/main/console';
7+
8+
export default rsbuildConfig({
9+
manifestPath: MANIFEST_PATH,
10+
rsbuild: ({ envMode }) => {
11+
const isProduction = envMode === 'production';
12+
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
13+
14+
return {
15+
resolve: {
16+
alias: {
17+
'@': './src',
18+
},
19+
},
20+
plugins: [
21+
pluginVue({
22+
vueLoaderOptions: {
23+
compilerOptions: {
24+
isCustomElement: (tag) => tag === 'comment-editor',
25+
},
26+
},
27+
}),
28+
],
29+
output: {
30+
distPath: {
31+
root: outDir,
32+
},
33+
},
34+
};
35+
},
36+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<script lang="ts" setup>
2+
import '@halo-dev/comment-widget';
3+
import type { CommentEditor } from '@halo-dev/comment-widget';
4+
import { onMounted, ref } from 'vue';
5+
6+
const props = defineProps<{
7+
autoFocus?: boolean;
8+
}>();
9+
10+
const emit =
11+
defineEmits<
12+
(e: 'update', content: { content: string; characterCount: number }) => void
13+
>();
14+
15+
const editorRef = ref<InstanceType<typeof CommentEditor>>();
16+
17+
onMounted(() => {
18+
if (props.autoFocus) {
19+
editorRef.value?.setFocus();
20+
}
21+
22+
editorRef.value?.addEventListener('update', (e) => {
23+
const detail = (e as CustomEvent).detail;
24+
emit('update', {
25+
content: detail.content,
26+
characterCount: detail.characterCount ?? 0,
27+
});
28+
});
29+
});
30+
</script>
31+
<template>
32+
<comment-editor ref="editorRef"></comment-editor>
33+
</template>

packages/ui/src/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { VLoading } from '@halo-dev/components';
2+
import { definePlugin } from '@halo-dev/console-shared';
3+
import { defineAsyncComponent, markRaw } from 'vue';
4+
5+
export default definePlugin({
6+
components: {},
7+
routes: [],
8+
extensionPoints: {
9+
'comment:editor:replace': () => {
10+
return {
11+
component: markRaw(
12+
defineAsyncComponent({
13+
loader: () => import('./components/Editor.vue'),
14+
loadingComponent: VLoading,
15+
})
16+
),
17+
};
18+
},
19+
},
20+
});

packages/ui/tsconfig.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": "@vue/tsconfig/tsconfig.dom.json",
3+
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4+
"exclude": ["src/**/__tests__/*"],
5+
"compilerOptions": {
6+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
7+
8+
"paths": {
9+
"@/*": ["./src/*"]
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)