@rolatech/angular-comment 17.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +7 -0
  2. package/esm2022/index.mjs +4 -0
  3. package/esm2022/lib/components/comment-action/comment-action.component.mjs +66 -0
  4. package/esm2022/lib/components/comment-item/comment-item.component.mjs +107 -0
  5. package/esm2022/lib/components/comment-replies/comment-replies.component.mjs +51 -0
  6. package/esm2022/lib/components/comments/comments.component.mjs +61 -0
  7. package/esm2022/lib/components/comments-header/comments-header.component.mjs +84 -0
  8. package/esm2022/lib/components/index.mjs +5 -0
  9. package/esm2022/lib/components/reply-item/reply-item.component.mjs +69 -0
  10. package/esm2022/lib/interfaces/comment.interface.mjs +6 -0
  11. package/esm2022/lib/interfaces/index.mjs +2 -0
  12. package/esm2022/lib/services/comment.service.mjs +200 -0
  13. package/esm2022/lib/services/index.mjs +2 -0
  14. package/esm2022/rolatech-angular-comment.mjs +5 -0
  15. package/fesm2022/rolatech-angular-comment.mjs +581 -0
  16. package/fesm2022/rolatech-angular-comment.mjs.map +1 -0
  17. package/index.d.ts +3 -0
  18. package/lib/components/comment-action/comment-action.component.d.ts +21 -0
  19. package/lib/components/comment-item/comment-item.component.d.ts +20 -0
  20. package/lib/components/comment-replies/comment-replies.component.d.ts +19 -0
  21. package/lib/components/comments/comments.component.d.ts +19 -0
  22. package/lib/components/comments-header/comments-header.component.d.ts +21 -0
  23. package/lib/components/index.d.ts +4 -0
  24. package/lib/components/reply-item/reply-item.component.d.ts +16 -0
  25. package/lib/interfaces/comment.interface.d.ts +43 -0
  26. package/lib/interfaces/index.d.ts +1 -0
  27. package/lib/services/comment.service.d.ts +37 -0
  28. package/lib/services/index.d.ts +1 -0
  29. package/package.json +31 -0
  30. package/themes/_default.scss +1 -0
@@ -0,0 +1,581 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, EventEmitter, Injectable, Component, Input, Output } from '@angular/core';
3
+ import * as i1$1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i2 from '@angular/material/button';
6
+ import { MatButtonModule } from '@angular/material/button';
7
+ import * as i2$1 from '@angular/material/icon';
8
+ import { MatIconModule } from '@angular/material/icon';
9
+ import * as i5 from '@angular/material/input';
10
+ import { MatInputModule } from '@angular/material/input';
11
+ import * as i4 from '@angular/material/form-field';
12
+ import { MatFormFieldModule } from '@angular/material/form-field';
13
+ import * as i1 from '@angular/forms';
14
+ import { FormsModule } from '@angular/forms';
15
+ import { AngularCommonModule, TimePipe } from '@rolatech/angular-common';
16
+ import * as i7 from '@angular/material/progress-spinner';
17
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
18
+ import { AuthService, AuthUserService } from '@rolatech/angular-auth';
19
+ import { BaseService } from '@rolatech/angular-services';
20
+ import _ from 'lodash';
21
+ import { map, switchMap, of } from 'rxjs';
22
+ import * as i6 from '@angular/cdk/text-field';
23
+ import * as i3 from '@angular/material/menu';
24
+ import { MatMenuModule } from '@angular/material/menu';
25
+
26
+ class CommentService extends BaseService {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.authService = inject(AuthService);
30
+ this.authUserService = inject(AuthUserService);
31
+ this.onCommentLoading = new EventEmitter();
32
+ this.onComment = new EventEmitter();
33
+ this.onCommentReply = new EventEmitter();
34
+ }
35
+ init() {
36
+ this.endpoint = 'comments';
37
+ super.init();
38
+ }
39
+ findComments(options) {
40
+ options = {
41
+ sort: 'createdAt desc',
42
+ };
43
+ return this.http.get(`${this.actionUrl}`, { params: options }).pipe(map((res) => {
44
+ const userIds = _.uniq(_.map(res.data, 'userId')); // [12, 14, 16, 18]
45
+ this.findCommentsUser(userIds);
46
+ return res;
47
+ }));
48
+ }
49
+ findCommentsByItemId(itemId) {
50
+ const options = {
51
+ sort: 'createdAt desc',
52
+ filter: `itemId:${itemId}`,
53
+ };
54
+ return this.http.get(`${this.actionUrl}`, { params: options }).pipe(switchMap((comments) => {
55
+ const userIds = _.uniq(_.map(comments.data, 'userId')); // [12, 14, 16, 18]
56
+ return this.findCommentsUser(userIds).pipe(map((users) => {
57
+ comments.data?.forEach((item) => {
58
+ const matchingUser = _.find(users.data, { id: item.userId });
59
+ item.user = matchingUser;
60
+ });
61
+ return comments;
62
+ }));
63
+ }), switchMap((comments) => {
64
+ const ids = _.uniq(_.map(comments.data, 'id')); // [12, 14, 16, 18]
65
+ return this.authService.introspect().pipe(switchMap((res) => {
66
+ if (res.authenticated) {
67
+ return this.findMyCommentsThumbs(ids).pipe(map((thumbs) => {
68
+ comments.data?.forEach((item) => {
69
+ const matchingComment = _.find(thumbs.data, (obj) => _.get(obj, `comment.id`) === item.id);
70
+ item.like = matchingComment?.status === 'UP';
71
+ item.dislike = matchingComment?.status === 'DOWN';
72
+ });
73
+ return comments;
74
+ }));
75
+ }
76
+ else {
77
+ return of(comments);
78
+ }
79
+ }));
80
+ }));
81
+ }
82
+ createComment(data) {
83
+ return this.http
84
+ .post(`${this.actionUrl}`, data, {
85
+ withCredentials: true,
86
+ })
87
+ .pipe(map((res) => {
88
+ res.data.repliesCount = 0;
89
+ this.getUserInfo(res.data);
90
+ return res.data;
91
+ }));
92
+ }
93
+ countCommnentsByItemId(itemId) {
94
+ return this.http.get(`${this.actionUrl}/count/by`, { params: { itemId } });
95
+ }
96
+ countRepliesByCommentId(commentId) {
97
+ return this.http.get(`${this.actionUrl}/replies/count/by`, { params: { commentId } });
98
+ }
99
+ reply(id, data) {
100
+ return this.http
101
+ .post(`${this.actionUrl}/${id}/replies`, data, {
102
+ withCredentials: true,
103
+ })
104
+ .pipe(map((res) => {
105
+ this.getReplyUserInfo(res.data);
106
+ return res.data;
107
+ }));
108
+ }
109
+ findRepliesByCommentId(commentId) {
110
+ return this.http.get(`${this.actionUrl}/${commentId}/replies`).pipe(switchMap((replies) => {
111
+ const userIds = _.uniq(_.map(replies.data, 'senderId')); // [12, 14, 16, 18]
112
+ return this.findRepliesUser(userIds).pipe(map((users) => {
113
+ replies.data?.forEach((item) => {
114
+ const matchingUser = _.find(users.data, { id: item.senderId });
115
+ item.user = matchingUser;
116
+ });
117
+ return replies;
118
+ }));
119
+ }), switchMap((replies) => {
120
+ const ids = _.uniq(_.map(replies.data, 'id'));
121
+ return this.authService.introspect().pipe(switchMap((res) => {
122
+ if (res.authenticated) {
123
+ return this.findMyRepliesThumbs(ids).pipe(map((thumbs) => {
124
+ replies.data?.forEach((reply) => {
125
+ const matchingReply = _.find(thumbs.data, (obj) => _.get(obj, `reply.id`) === reply.id);
126
+ reply.like = matchingReply?.status === 'UP';
127
+ reply.dislike = matchingReply?.status === 'DOWN';
128
+ });
129
+ return replies;
130
+ }));
131
+ }
132
+ else {
133
+ return of(replies);
134
+ }
135
+ }));
136
+ }));
137
+ }
138
+ commentThumbsUp(commentId) {
139
+ return this.http.post(`${this.actionUrl}/${commentId}/thumbs/up`, {}, {
140
+ withCredentials: true,
141
+ });
142
+ }
143
+ commentThumbsDown(commentId) {
144
+ return this.http.post(`${this.actionUrl}/${commentId}/thumbs/down`, {}, {
145
+ withCredentials: true,
146
+ });
147
+ }
148
+ deleteCommentThumbsUp(commentId) {
149
+ return this.http.delete(`${this.actionUrl}/${commentId}/thumbs/up`, {
150
+ withCredentials: true,
151
+ });
152
+ }
153
+ deleteCommentThumbsDown(commentId) {
154
+ return this.http.delete(`${this.actionUrl}/${commentId}/thumbs/down`, {
155
+ withCredentials: true,
156
+ });
157
+ }
158
+ replyThumbsUp(replyId) {
159
+ return this.http.post(`${this.actionUrl}/replies/${replyId}/thumbs/up`, {}, {
160
+ withCredentials: true,
161
+ });
162
+ }
163
+ replyThumbsDown(replyId) {
164
+ return this.http.post(`${this.actionUrl}/replies/${replyId}/thumbs/down`, {}, {
165
+ withCredentials: true,
166
+ });
167
+ }
168
+ deleteReplyThumbsUp(replyId) {
169
+ return this.http.delete(`${this.actionUrl}/${replyId}/thumbs/up`, {
170
+ withCredentials: true,
171
+ });
172
+ }
173
+ deleteReplyThumbsDown(replyId) {
174
+ return this.http.delete(`${this.actionUrl}/${replyId}/thumbs/down`, {
175
+ withCredentials: true,
176
+ });
177
+ }
178
+ getReplyUserInfo(reply) {
179
+ this.authUserService.getPublicUserInfo(reply.senderId).subscribe({
180
+ next: (res) => {
181
+ reply.user = res;
182
+ },
183
+ });
184
+ }
185
+ getUserInfo(comment) {
186
+ this.authUserService.getPublicUserInfo(comment.userId).subscribe({
187
+ next: (res) => {
188
+ comment.user = res;
189
+ },
190
+ });
191
+ }
192
+ findRepliesUser(ids) {
193
+ return this.authUserService.findPublicInfoByIds(ids);
194
+ }
195
+ findCommentsUser(ids) {
196
+ return this.authUserService.findPublicInfoByIds(ids);
197
+ }
198
+ findMyCommentsThumbs(ids) {
199
+ const params = { ids: ids.join(',') };
200
+ return this.http.get(`${this.actionUrl}/thumbs/me`, {
201
+ params: params,
202
+ withCredentials: true,
203
+ });
204
+ }
205
+ findMyRepliesThumbs(ids) {
206
+ const params = { ids: ids.join(',') };
207
+ return this.http.get(`${this.actionUrl}/replies/thumbs/me`, {
208
+ params: params,
209
+ withCredentials: true,
210
+ });
211
+ }
212
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
213
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentService, providedIn: 'root' }); }
214
+ }
215
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentService, decorators: [{
216
+ type: Injectable,
217
+ args: [{ providedIn: 'root' }]
218
+ }] });
219
+
220
+ class CommentActionComponent {
221
+ constructor() {
222
+ this.commentService = inject(CommentService);
223
+ this.placeholder = '请输入回复内容';
224
+ this.reply = new EventEmitter();
225
+ this.thumbsUp = new EventEmitter();
226
+ this.thumbsDown = new EventEmitter();
227
+ this.show = false;
228
+ this.content = '';
229
+ this.loading = false;
230
+ }
231
+ onCancel() {
232
+ this.show = false;
233
+ this.content = '';
234
+ }
235
+ onReply() {
236
+ this.loading = true;
237
+ this.reply.emit(this.content);
238
+ this.commentService.onCommentLoading.subscribe({
239
+ next: (res) => {
240
+ this.loading = res;
241
+ this.show = false;
242
+ this.content = '';
243
+ },
244
+ });
245
+ }
246
+ onThumbsUp() {
247
+ this.thumbsUp.emit();
248
+ }
249
+ onThumbsDown() {
250
+ this.thumbsDown.emit();
251
+ }
252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
253
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: CommentActionComponent, isStandalone: true, selector: "rolatech-comment-action", inputs: { placeholder: "placeholder", data: "data" }, outputs: { reply: "reply", thumbsUp: "thumbsUp", thumbsDown: "thumbsDown" }, ngImport: i0, template: "<div>\n <div class=\"flex items-center -ml-2\">\n <button class=\"w-8 h-8 p-1 hover:bg-gray-200 rounded-full\" (click)=\"onThumbsUp()\">\n <mat-icon [color]=\"data.like ? 'primary' : ''\">thumb_up</mat-icon>\n </button>\n @if (data.thumbsUpCount > 0) {\n <span class=\"text-sm opacity-80\">{{ data.thumbsUpCount }}</span>\n }\n <button class=\"w-8 h-8 p-1 hover:bg-gray-200 rounded-full\" (click)=\"onThumbsDown()\">\n <mat-icon [color]=\"data.dislike ? 'primary' : ''\">thumb_down</mat-icon>\n </button>\n @if (data.thumbsDownCount > 0) {\n <span class=\"text-sm opacity-80\">{{ data.thumbsDownCount }}</span>\n }\n\n <button mat-button (click)=\"show = true\" class=\"w-8 h-8\">\u56DE\u590D</button>\n </div>\n <div>\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n } @else {\n @if (show) {\n <mat-form-field>\n <textarea\n matInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"8\"\n [placeholder]=\"placeholder\"\n [(ngModel)]=\"content\"\n (focus)=\"show = true\"\n ></textarea>\n </mat-form-field>\n <div class=\"flex justify-end items-center gap-2 pr-2\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-flat-button color=\"primary\" (click)=\"onReply()\">\n <span class=\"text-white\">\u56DE\u590D</span>\n </button>\n </div>\n }\n }\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}mat-form-field{width:100%}textarea:focus{outline:none;box-shadow:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i6.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
254
+ }
255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentActionComponent, decorators: [{
256
+ type: Component,
257
+ args: [{ selector: 'rolatech-comment-action', standalone: true, imports: [AngularCommonModule, MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, MatProgressSpinnerModule], template: "<div>\n <div class=\"flex items-center -ml-2\">\n <button class=\"w-8 h-8 p-1 hover:bg-gray-200 rounded-full\" (click)=\"onThumbsUp()\">\n <mat-icon [color]=\"data.like ? 'primary' : ''\">thumb_up</mat-icon>\n </button>\n @if (data.thumbsUpCount > 0) {\n <span class=\"text-sm opacity-80\">{{ data.thumbsUpCount }}</span>\n }\n <button class=\"w-8 h-8 p-1 hover:bg-gray-200 rounded-full\" (click)=\"onThumbsDown()\">\n <mat-icon [color]=\"data.dislike ? 'primary' : ''\">thumb_down</mat-icon>\n </button>\n @if (data.thumbsDownCount > 0) {\n <span class=\"text-sm opacity-80\">{{ data.thumbsDownCount }}</span>\n }\n\n <button mat-button (click)=\"show = true\" class=\"w-8 h-8\">\u56DE\u590D</button>\n </div>\n <div>\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n } @else {\n @if (show) {\n <mat-form-field>\n <textarea\n matInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"8\"\n [placeholder]=\"placeholder\"\n [(ngModel)]=\"content\"\n (focus)=\"show = true\"\n ></textarea>\n </mat-form-field>\n <div class=\"flex justify-end items-center gap-2 pr-2\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-flat-button color=\"primary\" (click)=\"onReply()\">\n <span class=\"text-white\">\u56DE\u590D</span>\n </button>\n </div>\n }\n }\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}mat-form-field{width:100%}textarea:focus{outline:none;box-shadow:none}\n"] }]
258
+ }], propDecorators: { placeholder: [{
259
+ type: Input
260
+ }], data: [{
261
+ type: Input
262
+ }], reply: [{
263
+ type: Output
264
+ }], thumbsUp: [{
265
+ type: Output
266
+ }], thumbsDown: [{
267
+ type: Output
268
+ }] } });
269
+
270
+ class ReplyItemComponent {
271
+ constructor() {
272
+ this.commentService = inject(CommentService);
273
+ this.commentId = '';
274
+ this.replied = new EventEmitter();
275
+ this.show = false;
276
+ }
277
+ onReply(content) {
278
+ const data = {
279
+ replyId: this.reply.id,
280
+ content,
281
+ };
282
+ this.commentService.reply(this.commentId, data).subscribe({
283
+ next: (res) => {
284
+ this.commentService.onCommentLoading.emit(false);
285
+ this.replied.emit(res);
286
+ },
287
+ error: (error) => {
288
+ this.commentService.onCommentLoading.emit(false);
289
+ console.log(error);
290
+ },
291
+ });
292
+ }
293
+ onThumbsUp() {
294
+ this.commentService.replyThumbsUp(this.reply.id).subscribe({
295
+ next: (res) => {
296
+ this.reply.thumbsUpCount = res.data.thumbsUpCount;
297
+ this.reply.thumbsDownCount = res.data.thumbsDownCount;
298
+ this.reply.like = true;
299
+ this.reply.dislike = false;
300
+ },
301
+ });
302
+ }
303
+ onThumbsDown() {
304
+ this.commentService.replyThumbsDown(this.reply.id).subscribe({
305
+ next: (res) => {
306
+ this.reply.thumbsUpCount = res.data.thumbsUpCount;
307
+ this.reply.thumbsDownCount = res.data.thumbsDownCount;
308
+ this.reply.like = false;
309
+ this.reply.dislike = true;
310
+ },
311
+ });
312
+ }
313
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: ReplyItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
314
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: ReplyItemComponent, isStandalone: true, selector: "rolatech-reply-item", inputs: { reply: "reply", commentId: "commentId" }, outputs: { replied: "replied" }, ngImport: i0, template: "<div class=\"block w-full\">\n @if (reply) {\n <div class=\"flex group\">\n <div class=\"bg-orange-600 min-w-[28px] min-h-[28px] h-fit mr-3 rounded-full\">\n @if (reply.user && reply.user.avatar) {\n <img class=\"w-7 h-7 rounded-full\" [src]=\"reply.user.avatar\" />\n }\n </div>\n <div class=\"flex flex-col w-full\">\n <a class=\"mb-0.5\">\n @if (reply.user) {\n <span class=\"text-md font-semibold mr-1\">&#64;{{ reply.user.username }}</span>\n }\n <span class=\"text-sm opacity-70\">{{ reply.createdAt | time }}</span>\n </a>\n <div [innerText]=\"reply.content\"></div>\n <rolatech-comment-action\n (reply)=\"onReply($event)\"\n [data]=\"reply\"\n (thumbsUp)=\"onThumbsUp()\"\n (thumbsDown)=\"onThumbsDown()\"\n ></rolatech-comment-action>\n </div>\n <div class=\"min-w-10\">\n <button\n class=\"hover:bg-gray-200 w-9 h-9 flex items-center justify-center rounded-full group-hover:visible\"\n [ngClass]=\"memnu.menuOpen ? 'visible' : 'invisible'\"\n [matMenuTriggerFor]=\"actionMenu\"\n #memnu=\"matMenuTrigger\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n }\n</div>\n<mat-menu #actionMenu=\"matMenu\">\n <button mat-menu-item>\n <mat-icon>flag</mat-icon>\n <span>\u4E3E\u62A5</span>\n </button>\n</mat-menu>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: CommentActionComponent, selector: "rolatech-comment-action", inputs: ["placeholder", "data"], outputs: ["reply", "thumbsUp", "thumbsDown"] }, { kind: "pipe", type: TimePipe, name: "time" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }] }); }
315
+ }
316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: ReplyItemComponent, decorators: [{
317
+ type: Component,
318
+ args: [{ selector: 'rolatech-reply-item', standalone: true, imports: [CommonModule, MatButtonModule, MatIconModule, CommentActionComponent, TimePipe, MatMenuModule], template: "<div class=\"block w-full\">\n @if (reply) {\n <div class=\"flex group\">\n <div class=\"bg-orange-600 min-w-[28px] min-h-[28px] h-fit mr-3 rounded-full\">\n @if (reply.user && reply.user.avatar) {\n <img class=\"w-7 h-7 rounded-full\" [src]=\"reply.user.avatar\" />\n }\n </div>\n <div class=\"flex flex-col w-full\">\n <a class=\"mb-0.5\">\n @if (reply.user) {\n <span class=\"text-md font-semibold mr-1\">&#64;{{ reply.user.username }}</span>\n }\n <span class=\"text-sm opacity-70\">{{ reply.createdAt | time }}</span>\n </a>\n <div [innerText]=\"reply.content\"></div>\n <rolatech-comment-action\n (reply)=\"onReply($event)\"\n [data]=\"reply\"\n (thumbsUp)=\"onThumbsUp()\"\n (thumbsDown)=\"onThumbsDown()\"\n ></rolatech-comment-action>\n </div>\n <div class=\"min-w-10\">\n <button\n class=\"hover:bg-gray-200 w-9 h-9 flex items-center justify-center rounded-full group-hover:visible\"\n [ngClass]=\"memnu.menuOpen ? 'visible' : 'invisible'\"\n [matMenuTriggerFor]=\"actionMenu\"\n #memnu=\"matMenuTrigger\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n }\n</div>\n<mat-menu #actionMenu=\"matMenu\">\n <button mat-menu-item>\n <mat-icon>flag</mat-icon>\n <span>\u4E3E\u62A5</span>\n </button>\n</mat-menu>\n" }]
319
+ }], propDecorators: { reply: [{
320
+ type: Input
321
+ }], commentId: [{
322
+ type: Input
323
+ }], replied: [{
324
+ type: Output
325
+ }] } });
326
+
327
+ class CommentRepliesComponent {
328
+ constructor() {
329
+ this.commentService = inject(CommentService);
330
+ this.total = 0;
331
+ this.replies = [];
332
+ this.commentId = '';
333
+ this.replied = new EventEmitter();
334
+ this.fetch = new EventEmitter();
335
+ this.expand = false;
336
+ this.loading = false;
337
+ this.fetched = false;
338
+ }
339
+ onFetch() {
340
+ this.expand = !this.expand;
341
+ if (!this.fetched) {
342
+ this.fetched = true;
343
+ this.fetch.emit(this.commentId);
344
+ }
345
+ }
346
+ onItemReplied(reply) {
347
+ this.total++;
348
+ this.replies.push(reply);
349
+ }
350
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentRepliesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
351
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: CommentRepliesComponent, isStandalone: true, selector: "rolatech-comment-replies", inputs: { total: "total", replies: "replies", commentId: "commentId" }, outputs: { replied: "replied", fetch: "fetch" }, ngImport: i0, template: "<div class=\"block ml-14\">\n @if (total > 0) {\n <div>\n <button class=\"inline-flex items-center rounded-lg hover:bg-orange-200 py-1 -ml-1\" (click)=\"onFetch()\">\n <mat-icon color=\"primary\">{{ expand ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>\n <span class=\"mr-2 text-sm text-orange-500\">\u56DE\u590D({{ total || replies.length }})</span>\n </button>\n <!-- <div class=\"less-button\">\n <button (click)=\"expand = false\">222</button>\n </div> -->\n </div>\n }\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n }\n\n @if (expand) {\n @for (item of replies; track $index) {\n <rolatech-reply-item [commentId]=\"commentId\" [reply]=\"item\" (replied)=\"onItemReplied($event)\"></rolatech-reply-item>\n }\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: ReplyItemComponent, selector: "rolatech-reply-item", inputs: ["reply", "commentId"], outputs: ["replied"] }] }); }
352
+ }
353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentRepliesComponent, decorators: [{
354
+ type: Component,
355
+ args: [{ selector: 'rolatech-comment-replies', standalone: true, imports: [CommonModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule, ReplyItemComponent], template: "<div class=\"block ml-14\">\n @if (total > 0) {\n <div>\n <button class=\"inline-flex items-center rounded-lg hover:bg-orange-200 py-1 -ml-1\" (click)=\"onFetch()\">\n <mat-icon color=\"primary\">{{ expand ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>\n <span class=\"mr-2 text-sm text-orange-500\">\u56DE\u590D({{ total || replies.length }})</span>\n </button>\n <!-- <div class=\"less-button\">\n <button (click)=\"expand = false\">222</button>\n </div> -->\n </div>\n }\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n }\n\n @if (expand) {\n @for (item of replies; track $index) {\n <rolatech-reply-item [commentId]=\"commentId\" [reply]=\"item\" (replied)=\"onItemReplied($event)\"></rolatech-reply-item>\n }\n }\n</div>\n" }]
356
+ }], propDecorators: { total: [{
357
+ type: Input
358
+ }], replies: [{
359
+ type: Input
360
+ }], commentId: [{
361
+ type: Input
362
+ }], replied: [{
363
+ type: Output
364
+ }], fetch: [{
365
+ type: Output
366
+ }] } });
367
+
368
+ class CommentItemComponent {
369
+ constructor() {
370
+ this.commentService = inject(CommentService);
371
+ this.show = false;
372
+ this.loading = false;
373
+ this.replies = [];
374
+ }
375
+ ngOnInit() {
376
+ // this.countRepliesByCommentId(this.comment.id!);
377
+ }
378
+ onFocus(event) {
379
+ this.show = true;
380
+ }
381
+ onCommentReply(content) {
382
+ const data = {
383
+ content,
384
+ };
385
+ const id = this.comment.id;
386
+ this.commentService.reply(id, data).subscribe({
387
+ next: (res) => {
388
+ this.comment.repliesCount++;
389
+ this.comment.replies = this.comment.replies ? this.comment.replies : [];
390
+ this.comment.replies.push(res);
391
+ this.commentService.onCommentLoading.emit(false);
392
+ this.commentService.onCommentReply.emit(res);
393
+ },
394
+ error: (error) => {
395
+ this.commentService.onCommentLoading.emit(false);
396
+ },
397
+ });
398
+ }
399
+ countRepliesByCommentId(commentId) {
400
+ this.commentService.countRepliesByCommentId(commentId).subscribe({
401
+ next: (res) => {
402
+ this.comment.repliesCount = res.data;
403
+ },
404
+ error: (error) => { },
405
+ });
406
+ }
407
+ onFetchReplies(commentId) {
408
+ this.loading = true;
409
+ this.commentService.findRepliesByCommentId(commentId).subscribe({
410
+ next: (res) => {
411
+ this.comment.replies = res.data;
412
+ },
413
+ error: (error) => {
414
+ this.loading = false;
415
+ },
416
+ });
417
+ }
418
+ onThumbsUp() {
419
+ this.commentService.commentThumbsUp(this.comment.id).subscribe({
420
+ next: (res) => {
421
+ this.comment.thumbsUpCount = res.data.thumbsUpCount;
422
+ this.comment.thumbsDownCount = res.data.thumbsDownCount;
423
+ this.comment.like = true;
424
+ this.comment.dislike = false;
425
+ },
426
+ });
427
+ }
428
+ onThumbsDown() {
429
+ this.commentService.commentThumbsDown(this.comment.id).subscribe({
430
+ next: (res) => {
431
+ this.comment.thumbsUpCount = res.data.thumbsUpCount;
432
+ this.comment.thumbsDownCount = res.data.thumbsDownCount;
433
+ this.comment.like = false;
434
+ this.comment.dislike = true;
435
+ },
436
+ });
437
+ }
438
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
439
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: CommentItemComponent, isStandalone: true, selector: "rolatech-comment-item", inputs: { comment: "comment" }, ngImport: i0, template: "<div class=\"block w-full\">\n @if (comment) {\n <div class=\"flex group\">\n <div class=\"bg-orange-600 min-w-10 min-h-10 mr-4 h-fit rounded-full\">\n @if (comment.user && comment.user.avatar) {\n <img class=\"w-10 h-10 rounded-full\" [src]=\"comment.user.avatar\" />\n }\n </div>\n <div class=\"flex flex-col w-full\">\n <a class=\"mb-0.5\">\n @if (comment.user) {\n <span class=\"text-md font-semibold mr-1\">&#64;{{ comment.user.username }}</span>\n }\n <span class=\"text-sm opacity-70\">{{ comment.createdAt | time }}</span>\n </a>\n\n <div [innerText]=\"comment.content\"></div>\n <rolatech-comment-action\n (reply)=\"onCommentReply($event)\"\n [data]=\"comment\"\n (thumbsUp)=\"onThumbsUp()\"\n (thumbsDown)=\"onThumbsDown()\"\n ></rolatech-comment-action>\n </div>\n <div class=\"min-w-9\">\n <button\n class=\"hover:bg-gray-200 w-9 h-9 flex items-center justify-center rounded-full group-hover:visible\"\n [ngClass]=\"memnu.menuOpen ? 'visible' : 'invisible'\"\n [matMenuTriggerFor]=\"actionMenu\"\n #memnu=\"matMenuTrigger\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n <div>\n <rolatech-comment-replies\n [total]=\"comment.repliesCount\"\n [commentId]=\"comment.id!\"\n [replies]=\"comment.replies!\"\n (fetch)=\"onFetchReplies($event)\"\n ></rolatech-comment-replies>\n </div>\n }\n</div>\n<mat-menu #actionMenu=\"matMenu\">\n <button mat-menu-item>\n <mat-icon>flag</mat-icon>\n <span>\u4E3E\u62A5</span>\n </button>\n</mat-menu>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "component", type: CommentRepliesComponent, selector: "rolatech-comment-replies", inputs: ["total", "replies", "commentId"], outputs: ["replied", "fetch"] }, { kind: "component", type: CommentActionComponent, selector: "rolatech-comment-action", inputs: ["placeholder", "data"], outputs: ["reply", "thumbsUp", "thumbsDown"] }, { kind: "pipe", type: TimePipe, name: "time" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }] }); }
440
+ }
441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentItemComponent, decorators: [{
442
+ type: Component,
443
+ args: [{ selector: 'rolatech-comment-item', standalone: true, imports: [
444
+ CommonModule,
445
+ MatButtonModule,
446
+ MatIconModule,
447
+ FormsModule,
448
+ MatFormFieldModule,
449
+ MatInputModule,
450
+ CommentRepliesComponent,
451
+ CommentActionComponent,
452
+ TimePipe,
453
+ MatMenuModule,
454
+ ], template: "<div class=\"block w-full\">\n @if (comment) {\n <div class=\"flex group\">\n <div class=\"bg-orange-600 min-w-10 min-h-10 mr-4 h-fit rounded-full\">\n @if (comment.user && comment.user.avatar) {\n <img class=\"w-10 h-10 rounded-full\" [src]=\"comment.user.avatar\" />\n }\n </div>\n <div class=\"flex flex-col w-full\">\n <a class=\"mb-0.5\">\n @if (comment.user) {\n <span class=\"text-md font-semibold mr-1\">&#64;{{ comment.user.username }}</span>\n }\n <span class=\"text-sm opacity-70\">{{ comment.createdAt | time }}</span>\n </a>\n\n <div [innerText]=\"comment.content\"></div>\n <rolatech-comment-action\n (reply)=\"onCommentReply($event)\"\n [data]=\"comment\"\n (thumbsUp)=\"onThumbsUp()\"\n (thumbsDown)=\"onThumbsDown()\"\n ></rolatech-comment-action>\n </div>\n <div class=\"min-w-9\">\n <button\n class=\"hover:bg-gray-200 w-9 h-9 flex items-center justify-center rounded-full group-hover:visible\"\n [ngClass]=\"memnu.menuOpen ? 'visible' : 'invisible'\"\n [matMenuTriggerFor]=\"actionMenu\"\n #memnu=\"matMenuTrigger\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n <div>\n <rolatech-comment-replies\n [total]=\"comment.repliesCount\"\n [commentId]=\"comment.id!\"\n [replies]=\"comment.replies!\"\n (fetch)=\"onFetchReplies($event)\"\n ></rolatech-comment-replies>\n </div>\n }\n</div>\n<mat-menu #actionMenu=\"matMenu\">\n <button mat-menu-item>\n <mat-icon>flag</mat-icon>\n <span>\u4E3E\u62A5</span>\n </button>\n</mat-menu>\n", styles: ["mat-form-field{width:100%}\n"] }]
455
+ }], propDecorators: { comment: [{
456
+ type: Input
457
+ }] } });
458
+
459
+ class CommentsHeaderComponent {
460
+ constructor() {
461
+ this.commentService = inject(CommentService);
462
+ this.comments = [];
463
+ this.placeholder = '请输入评论内容';
464
+ this.total = 0;
465
+ this.comment = new EventEmitter();
466
+ this.content = '';
467
+ this.show = false;
468
+ this.loading = false;
469
+ }
470
+ ngOnInit() {
471
+ this.commentService.onComment.subscribe({
472
+ next: (res) => {
473
+ this.total++;
474
+ },
475
+ });
476
+ }
477
+ onFocus(event) {
478
+ this.show = true;
479
+ }
480
+ onCancel() {
481
+ this.show = false;
482
+ this.content = '';
483
+ }
484
+ onComment() {
485
+ this.loading = true;
486
+ this.comment.emit(this.content);
487
+ this.commentService.onCommentLoading.subscribe({
488
+ next: (res) => {
489
+ this.loading = res;
490
+ this.show = false;
491
+ this.content = '';
492
+ },
493
+ });
494
+ }
495
+ fetchByComments() { }
496
+ fetchByReplies() { }
497
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentsHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
498
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: CommentsHeaderComponent, isStandalone: true, selector: "rolatech-comments-header", inputs: { comments: "comments", placeholder: "placeholder", total: "total" }, outputs: { comment: "comment" }, ngImport: i0, template: "<div class=\"my-2\">\n <div class=\"flex items-center gap-3 py-2\">\n <span class=\"text-lg font-bold\">\u8BC4\u8BBA({{ total }})</span>\n <button mat-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>sort</mat-icon>\n <span>\u6392\u5E8F</span>\n </button>\n </div>\n\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n } @else {\n <div class=\"mr-3\">\n <mat-form-field>\n <textarea\n matInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"8\"\n [placeholder]=\"placeholder\"\n [(ngModel)]=\"content\"\n (focus)=\"onFocus($event)\"\n ></textarea>\n </mat-form-field>\n @if (show) {\n <div class=\"flex justify-end items-center gap-2 pr-2\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-flat-button color=\"primary\" (click)=\"onComment()\">\n <span class=\"text-white\">\u8BC4\u8BBA</span>\n </button>\n </div>\n }\n </div>\n }\n</div>\n<mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"fetchByReplies()\">\u6700\u591A\u56DE\u590D</button>\n <button mat-menu-item (click)=\"fetchByComments()\">\u6700\u65B0\u8BC4\u8BBA</button>\n</mat-menu>\n", styles: ["mat-form-field{width:100%}textarea:focus{outline:none;box-shadow:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i6.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
499
+ }
500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentsHeaderComponent, decorators: [{
501
+ type: Component,
502
+ args: [{ selector: 'rolatech-comments-header', standalone: true, imports: [
503
+ AngularCommonModule,
504
+ CommentItemComponent,
505
+ MatButtonModule,
506
+ MatIconModule,
507
+ MatMenuModule,
508
+ MatFormFieldModule,
509
+ MatInputModule,
510
+ CommentActionComponent,
511
+ MatProgressSpinnerModule,
512
+ ], template: "<div class=\"my-2\">\n <div class=\"flex items-center gap-3 py-2\">\n <span class=\"text-lg font-bold\">\u8BC4\u8BBA({{ total }})</span>\n <button mat-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>sort</mat-icon>\n <span>\u6392\u5E8F</span>\n </button>\n </div>\n\n @if (loading) {\n <div class=\"flex items-center justify-center h-20\">\n <mat-spinner diameter=\"32\"></mat-spinner>\n </div>\n } @else {\n <div class=\"mr-3\">\n <mat-form-field>\n <textarea\n matInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"8\"\n [placeholder]=\"placeholder\"\n [(ngModel)]=\"content\"\n (focus)=\"onFocus($event)\"\n ></textarea>\n </mat-form-field>\n @if (show) {\n <div class=\"flex justify-end items-center gap-2 pr-2\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-flat-button color=\"primary\" (click)=\"onComment()\">\n <span class=\"text-white\">\u8BC4\u8BBA</span>\n </button>\n </div>\n }\n </div>\n }\n</div>\n<mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"fetchByReplies()\">\u6700\u591A\u56DE\u590D</button>\n <button mat-menu-item (click)=\"fetchByComments()\">\u6700\u65B0\u8BC4\u8BBA</button>\n</mat-menu>\n", styles: ["mat-form-field{width:100%}textarea:focus{outline:none;box-shadow:none}\n"] }]
513
+ }], propDecorators: { comments: [{
514
+ type: Input
515
+ }], placeholder: [{
516
+ type: Input
517
+ }], total: [{
518
+ type: Input
519
+ }], comment: [{
520
+ type: Output
521
+ }] } });
522
+
523
+ class CommentsComponent {
524
+ constructor() {
525
+ this.commentService = inject(CommentService);
526
+ this.authUserService = inject(AuthUserService);
527
+ this.comments = [];
528
+ this.itemId = '';
529
+ this.total = 0;
530
+ this.show = false;
531
+ this.content = '';
532
+ }
533
+ ngOnChanges(changes) {
534
+ const itemId = changes['itemId'].currentValue;
535
+ this.find(itemId);
536
+ }
537
+ find(itemId) {
538
+ this.commentService.findCommentsByItemId(itemId).subscribe({
539
+ next: (res) => {
540
+ this.total = res.meta?.pagination.count;
541
+ this.comments = res.data;
542
+ },
543
+ });
544
+ }
545
+ onComment(content) {
546
+ const data = {
547
+ itemId: this.itemId,
548
+ content,
549
+ };
550
+ this.commentService.createComment(data).subscribe({
551
+ next: (res) => {
552
+ this.comments = this.comments ? this.comments : [];
553
+ this.comments.unshift(res);
554
+ this.commentService.onCommentLoading.emit(false);
555
+ this.commentService.onComment.emit(true);
556
+ },
557
+ error: (error) => {
558
+ console.log(error);
559
+ },
560
+ });
561
+ }
562
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
563
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.1", type: CommentsComponent, isStandalone: true, selector: "rolatech-comments", inputs: { comments: "comments", itemId: "itemId", total: "total" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\" id=\"comment\">\n <rolatech-comments-header (comment)=\"onComment($event)\" [total]=\"total\"></rolatech-comments-header>\n @for (item of comments; track $index) {\n <rolatech-comment-item [comment]=\"item\"></rolatech-comment-item>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "component", type: CommentItemComponent, selector: "rolatech-comment-item", inputs: ["comment"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: CommentsHeaderComponent, selector: "rolatech-comments-header", inputs: ["comments", "placeholder", "total"], outputs: ["comment"] }] }); }
564
+ }
565
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.1", ngImport: i0, type: CommentsComponent, decorators: [{
566
+ type: Component,
567
+ args: [{ selector: 'rolatech-comments', standalone: true, imports: [AngularCommonModule, CommentItemComponent, MatButtonModule, CommentsHeaderComponent], template: "<div class=\"flex flex-col gap-3\" id=\"comment\">\n <rolatech-comments-header (comment)=\"onComment($event)\" [total]=\"total\"></rolatech-comments-header>\n @for (item of comments; track $index) {\n <rolatech-comment-item [comment]=\"item\"></rolatech-comment-item>\n }\n</div>\n" }]
568
+ }], propDecorators: { comments: [{
569
+ type: Input
570
+ }], itemId: [{
571
+ type: Input
572
+ }], total: [{
573
+ type: Input
574
+ }] } });
575
+
576
+ /**
577
+ * Generated bundle index. Do not edit.
578
+ */
579
+
580
+ export { CommentItemComponent, CommentRepliesComponent, CommentService, CommentsComponent, ReplyItemComponent };
581
+ //# sourceMappingURL=rolatech-angular-comment.mjs.map