From f4f0712ae332ff0865968ccb49c80e443528b327 Mon Sep 17 00:00:00 2001 From: Ivan Monastyrsky Date: Thu, 21 Oct 2021 23:33:56 +0700 Subject: [PATCH] ChatMessage: Added a new component for chat messages --- components.json | 3 +- examples/docs/en-US/chat-message.md | 223 ++++++++++++++++++ examples/docs/es/chat-message.md | 1 + examples/docs/fr-FR/chat-message.md | 1 + examples/docs/zh-CN/chat-message.md | 1 + examples/nav.config.json | 16 ++ packages/chat-message/index.js | 8 + packages/chat-message/src/chat-message.vue | 111 +++++++++ packages/theme-chalk/src/chat-message.scss | 143 +++++++++++ packages/theme-chalk/src/common/var.scss | 93 +++++++- packages/theme-chalk/src/index.scss | 1 + .../theme-chalk/src/mixins/_chat-message.scss | 106 +++++++++ src/index.js | 5 +- test/unit/specs/chat-message.spec.js | 51 ++++ types/chat-message.d.ts | 44 ++++ types/element-ui.d.ts | 6 +- 16 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 examples/docs/en-US/chat-message.md create mode 100644 examples/docs/es/chat-message.md create mode 100644 examples/docs/fr-FR/chat-message.md create mode 100644 examples/docs/zh-CN/chat-message.md create mode 100644 packages/chat-message/index.js create mode 100644 packages/chat-message/src/chat-message.vue create mode 100644 packages/theme-chalk/src/chat-message.scss create mode 100644 packages/theme-chalk/src/mixins/_chat-message.scss create mode 100644 test/unit/specs/chat-message.spec.js create mode 100644 types/chat-message.d.ts diff --git a/components.json b/components.json index 62d748707..36137a640 100644 --- a/components.json +++ b/components.json @@ -86,5 +86,6 @@ "empty": "./packages/empty/index.js", "descriptions": "./packages/descriptions/index.js", "descriptions-item": "./packages/descriptions-item/index.js", - "result": "./packages/result/index.js" + "result": "./packages/result/index.js", + "chat-message": "./packages/chat-message/index.js" } diff --git a/examples/docs/en-US/chat-message.md b/examples/docs/en-US/chat-message.md new file mode 100644 index 000000000..25ccef0d2 --- /dev/null +++ b/examples/docs/en-US/chat-message.md @@ -0,0 +1,223 @@ +## ChatMessage + +Message component for creating full-fledged chats + +### Basic usage + +:::demo Use `type` and `size` to define style and size +```html + + + + + + + + +``` +::: + +### Positioning + +:::demo Use `position` to define the side of the correspondence +```html + + +``` +::: + +### Personalization + +:::demo Use `name` and `avatar` to personalize the message +```html + + +``` +::: + +### Status and time stamp + +You can add one of the statuses `sending`, `delivered`, `read` and time to the message + +:::demo Use `status` and` stamp` to set time and status +```html + + + + +``` +::: + +### Message Stacks + +Consecutive messages from the same person can be displayed in a grouped stack + +:::demo Now we pass an array of messages into `text` +```html + + + +``` +::: + +### Slots + +Using slots in message elements + +:::demo +```html + + + +``` +::: + +### Attributes +| Attribute | Description | Type | Accepted values | Default | +|-----------|--------------------------------- |--------------- |-----------------------------------------|-------- | +| size | Message size | string | large / default / medium / small / mini | default | +| type | Message type | string | primary / success / warning / danger | — | +| text | Message or stack of messages | string / array | — | — | +| name | Username | string | — | — | +| avatar | Avatar url | string | — | — | +| stamp | Time stamp | string | — | — | +| status | Message status | string | none / sending / delivered / read | none | +| position | Side of the message box | string | left / right | left | + +### Scoped Slot +| Name | Description | +|---------- |--------------------------------------------------------------- | +| name | Username. The scope parameter is { row: { name }, $index } | +| avatar | User avatar. The scope parameter is { row: { avatar }, $index } | +| text | Message Text. The scope parameter is { message, $index } | +| stamp | Time stamp. The scope parameter is { message, $index } | +| status | Message status. The scope parameter is { message, $index } | diff --git a/examples/docs/es/chat-message.md b/examples/docs/es/chat-message.md new file mode 100644 index 000000000..e6ea217cc --- /dev/null +++ b/examples/docs/es/chat-message.md @@ -0,0 +1 @@ +## ChatMessage diff --git a/examples/docs/fr-FR/chat-message.md b/examples/docs/fr-FR/chat-message.md new file mode 100644 index 000000000..e6ea217cc --- /dev/null +++ b/examples/docs/fr-FR/chat-message.md @@ -0,0 +1 @@ +## ChatMessage diff --git a/examples/docs/zh-CN/chat-message.md b/examples/docs/zh-CN/chat-message.md new file mode 100644 index 000000000..70ca72946 --- /dev/null +++ b/examples/docs/zh-CN/chat-message.md @@ -0,0 +1 @@ +## ChatMessage chat-message diff --git a/examples/nav.config.json b/examples/nav.config.json index 3377ac933..e330f1479 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -307,6 +307,10 @@ { "path": "/drawer", "title": "Drawer 抽屉" + }, + { + "path": "/chat-message", + "title": "ChatMessage" } ] } @@ -621,6 +625,10 @@ { "path": "/drawer", "title": "Drawer" + }, + { + "path": "/chat-message", + "title": "ChatMessage" } ] } @@ -935,6 +943,10 @@ { "path": "/drawer", "title": "Drawer" + }, + { + "path": "/chat-message", + "title": "ChatMessage" } ] } @@ -1249,6 +1261,10 @@ { "path": "/drawer", "title": "Drawer" + }, + { + "path": "/chat-message", + "title": "ChatMessage" } ] } diff --git a/packages/chat-message/index.js b/packages/chat-message/index.js new file mode 100644 index 000000000..a82fc9711 --- /dev/null +++ b/packages/chat-message/index.js @@ -0,0 +1,8 @@ +import ChatMessage from './src/chat-message'; + +/* istanbul ignore next */ +ChatMessage.install = function(Vue) { + Vue.component(ChatMessage.name, ChatMessage); +}; + +export default ChatMessage; diff --git a/packages/chat-message/src/chat-message.vue b/packages/chat-message/src/chat-message.vue new file mode 100644 index 000000000..49dc04178 --- /dev/null +++ b/packages/chat-message/src/chat-message.vue @@ -0,0 +1,111 @@ + + + diff --git a/packages/theme-chalk/src/chat-message.scss b/packages/theme-chalk/src/chat-message.scss new file mode 100644 index 000000000..1bad2ef6f --- /dev/null +++ b/packages/theme-chalk/src/chat-message.scss @@ -0,0 +1,143 @@ +@import "mixins/mixins"; +@import "mixins/chat-message"; +@import "common/var"; + +@include b(chat-message) { + @include e(list) { + display: flex; + flex-direction: column; + + @include e(box) { + display: flex; + align-items: flex-end; + + @include e(body) { + color: $--chat-message--color; + background-color: $--color-info-light; + + @include e(name) { + margin-bottom: 2px; + font-weight: bold; + } + + @include e(footer) { + float: right; + display: flex; + justify-content: flex-end; + + color: darken($--color-info-light, 20); + + @include e(check-icon){ + color: #11c711; + position: relative; + + .el-icon-check { + font-weight: bold; + } + + } + } + } + } + } + + @include when(left) { + @include e(list) { + align-items: flex-start; + } + + @include e(name) { + text-align: left; + } + } + + @include when(right) { + @include e(list) { + align-items: flex-end; + + @include e(box) { + flex-direction: row-reverse; + } + } + + @include e(name) { + text-align: right; + } + } + + @include m('primary') { + @include chat-message-variant($--chat-message-primary-font-color, $--chat-message-primary-background-color) + } + + @include m('success') { + @include chat-message-variant($--chat-message-success-font-color, $--chat-message-success-background-color) + } + + @include m('warning') { + @include chat-message-variant($--chat-message-warning-font-color, $--chat-message-warning-background-color) + } + + @include m('danger') { + @include chat-message-variant($--chat-message-danger-font-color, $--chat-message-danger-background-color) + } + + @include m('default') { + @include chat-message-size( + $--chat-message-avatar-size, + $--chat-message-margin-gutter, + $--chat-message-font-size, + $--chat-message-font-size-footer, + $--chat-message-padding-vertical, + $--chat-message-padding-horizontal, + $--chat-message-border-radius + ) + } + + @include m('large') { + @include chat-message-size( + $--chat-message-large-avatar-size, + $--chat-message-large-margin-gutter, + $--chat-message-large-font-size, + $--chat-message-large-font-size-footer, + $--chat-message-large-padding-vertical, + $--chat-message-large-padding-horizontal, + $--chat-message-large-border-radius + ) + } + + @include m('medium') { + @include chat-message-size( + $--chat-message-medium-avatar-size, + $--chat-message-medium-margin-gutter, + $--chat-message-medium-font-size, + $--chat-message-medium-font-size-footer, + $--chat-message-medium-padding-vertical, + $--chat-message-medium-padding-horizontal, + $--chat-message-medium-border-radius + ) + } + + @include m('small') { + @include chat-message-size( + $--chat-message-small-avatar-size, + $--chat-message-small-margin-gutter, + $--chat-message-small-font-size, + $--chat-message-small-font-size-footer, + $--chat-message-small-padding-vertical, + $--chat-message-small-padding-horizontal, + $--chat-message-small-border-radius + ) + } + + @include m('mini') { + @include chat-message-size( + $--chat-message-mini-avatar-size, + $--chat-message-mini-margin-gutter, + $--chat-message-mini-font-size, + $--chat-message-mini-font-size-footer, + $--chat-message-mini-padding-vertical, + $--chat-message-mini-padding-horizontal, + $--chat-message-mini-border-radius + ) + } +} diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 142838c05..bcd530f29 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -147,6 +147,97 @@ $--disabled-border-base: $--border-color-light !default; $--icon-color: #666 !default; $--icon-color-base: $--color-info !default; +/* ChatMessage +-------------------------- */ +$--chat-message--color: $--color-black !default; + +$--icon-color-base: $--color-info !default; + + +$--chat-message-avatar-size: 40px; +$--chat-message-margin-gutter: 10px; +$--chat-message-font-size-footer: 12px; +/// fontSize||Font|1 +$--chat-message-font-size: $--font-size-base !default; +/// borderRadius||Border|2 +$--chat-message-border-radius: 20px; +/// padding||Spacing|3 +$--chat-message-padding-vertical: 12px !default; +/// padding||Spacing|3 +$--chat-message-padding-horizontal: 20px !default; + + +$--chat-message-large-avatar-size: 50px; +$--chat-message-large-margin-gutter: 10px; +$--chat-message-large-font-size-footer: 13px; +/// fontSize||Font|1 +$--chat-message-large-font-size: 15px !default; +/// borderRadius||Border|2 +$--chat-message-large-border-radius: 20px; +/// padding||Spacing|3 +$--chat-message-large-padding-vertical: 14px !default; +/// padding||Spacing|3 +$--chat-message-large-padding-horizontal: 20px !default; + + +$--chat-message-medium-avatar-size: 36px; +$--chat-message-medium-margin-gutter: 10px; +$--chat-message-medium-font-size-footer: 11px; +/// fontSize||Font|1 +$--chat-message-medium-font-size: 13px !default; +/// borderRadius||Border|2 +$--chat-message-medium-border-radius: 20px; +/// padding||Spacing|3 +$--chat-message-medium-padding-vertical: 6px !default; +/// padding||Spacing|3 +$--chat-message-medium-padding-horizontal: 14px !default; + + +$--chat-message-small-avatar-size: 30px; +$--chat-message-small-margin-gutter: 8px; +$--chat-message-small-font-size-footer: 11px; +/// fontSize||Font|1 +$--chat-message-small-font-size: 13px !default; +/// borderRadius||Border|2 +$--chat-message-small-border-radius: 20px; +/// padding||Spacing|3 +$--chat-message-small-padding-vertical: 6px !default; +/// padding||Spacing|3 +$--chat-message-small-padding-horizontal: 14px !default; + + +$--chat-message-mini-avatar-size: 26px; +$--chat-message-mini-margin-gutter: 6px; +$--chat-message-mini-font-size-footer: 11px; +/// fontSize||Font|1 +$--chat-message-mini-font-size: 12px !default; +/// borderRadius||Border|2 +$--chat-message-mini-border-radius: 20px; +/// padding||Spacing|3 +$--chat-message-mini-padding-vertical: 6px !default; +/// padding||Spacing|3 +$--chat-message-mini-padding-horizontal: 10px !default; + +/// color||Color|0 +$--chat-message-primary-font-color: $--color-white !default; +/// color||Color|0 +$--chat-message-primary-background-color: $--color-primary !default; +/// color||Color|0 +$--chat-message-success-font-color: $--color-white !default; +/// color||Color|0 +$--chat-message-success-background-color: $--color-success !default; +/// color||Color|0 +$--chat-message-warning-font-color: $--color-white !default; +/// color||Color|0 +$--chat-message-warning-background-color: $--color-warning !default; +/// color||Color|0 +$--chat-message-danger-font-color: $--color-white !default; +/// color||Color|0 +$--chat-message-danger-background-color: $--color-danger !default; + +$--chat-message-hover-tint-percent: 20% !default; +$--chat-message-active-shade-percent: 10% !default; + /* Checkbox -------------------------- */ /// fontSize||Font|1 @@ -978,7 +1069,7 @@ $--descriptions-title-font-size: 16px !default; $--descriptions-table-border: 1px solid $--border-color-lighter !default; $--descriptions-item-bordered-label-background: #fafafa !default; -/* Skeleton +/* Skeleton --------------------------*/ $--skeleton-color: #f2f2f2 !default; $--skeleton-to-color: #e6e6e6 !default; diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 0d81c5311..3e9f79d08 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -84,3 +84,4 @@ @import "./descriptions.scss"; @import "./descriptions-item.scss"; @import "./result.scss"; +@import "./chat-message.scss"; diff --git a/packages/theme-chalk/src/mixins/_chat-message.scss b/packages/theme-chalk/src/mixins/_chat-message.scss new file mode 100644 index 000000000..b5bb9674b --- /dev/null +++ b/packages/theme-chalk/src/mixins/_chat-message.scss @@ -0,0 +1,106 @@ +@import "../common/var"; +@import "mixins"; + +@mixin chat-message-variant($color, $background-color) { + @include e(list) { + @include e(box) { + @include e(body) { + color: $color; + background-color: $background-color; + + @include e(footer) { + color: mix($--color-white, $background-color, 70); + + @include e(check-icon){ + color: mix($--color-white, $background-color, 70); + } + } + } + } + } +} + +@mixin chat-message-size( + $avatar-size, + $margin-gutter, + $font-size, + $font-size-footer, + $padding-vertical, + $padding-horizontal, + $border-radius +) { + @include e(list) { + @include e(box) { + margin-bottom: $margin-gutter; + @include e(avatar) { + width: $avatar-size; + margin-right: $margin-gutter; + + .el-avatar { + width: $avatar-size; + height: $avatar-size; + } + } + + @include e(body) { + padding: $padding-vertical $padding-horizontal; + border-radius: $border-radius; + font-size: $font-size; + + @include e(name) { + margin-bottom: 2px; + font-size: $font-size; + font-weight: bold; + } + + @include e(footer) { + font-size: $font-size-footer; + margin-left: 10px; + line-height: $font-size + 2; + + @include pseudo(last-child) { + @include e(check-icon) { + .el-icon-check.second { + position: absolute; + left: 4px; + top: $font-size - $font-size-footer; + } + } + } + } + } + } + } + + @include when(left) { + @include e(list) { + @include e(box) { + @include e(avatar) { + margin-left: $margin-gutter; + } + + @include pseudo(first-child) { + @include e(body) { + border-radius: $border-radius $border-radius $border-radius 5px; + } + } + } + } + } + + @include when(right) { + @include e(list) { + @include e(box) { + @include e(avatar) { + margin-left: $margin-gutter; + } + + @include pseudo(first-child) { + @include e(body) { + border-radius: $border-radius $border-radius 5px $border-radius; + } + } + } + } + } +} diff --git a/src/index.js b/src/index.js index b32222f6f..b93d1eaf1 100644 --- a/src/index.js +++ b/src/index.js @@ -88,6 +88,7 @@ import Empty from '../packages/empty/index.js'; import Descriptions from '../packages/descriptions/index.js'; import DescriptionsItem from '../packages/descriptions-item/index.js'; import Result from '../packages/result/index.js'; +import ChatMessage from '../packages/chat-message/index.js'; import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; @@ -175,6 +176,7 @@ const components = [ Descriptions, DescriptionsItem, Result, + ChatMessage, CollapseTransition ]; @@ -302,5 +304,6 @@ export default { Empty, Descriptions, DescriptionsItem, - Result + Result, + ChatMessage }; diff --git a/test/unit/specs/chat-message.spec.js b/test/unit/specs/chat-message.spec.js new file mode 100644 index 000000000..96f6dc355 --- /dev/null +++ b/test/unit/specs/chat-message.spec.js @@ -0,0 +1,51 @@ +import { createTest, destroyVM } from '../util'; +import ChatMessage from 'packages/chat-message'; + +const testProps = { + text: 'Test text', + name: 'Evan You' +}; + +describe('ChatMessage', () => { + let vm; + afterEach(() => { + destroyVM(vm); + }); + + it('create', () => { + vm = createTest(ChatMessage, { + ...testProps, + type: 'primary' + }, true); + let messageElm = vm.$el; + expect(messageElm.classList.contains('el-chat-message--primary')).to.be.true; + }); + + it('size medium', () => { + vm = createTest(ChatMessage, { + ...testProps, + size: 'medium' + }, true); + let messageElm = vm.$el; + expect(messageElm.classList.contains('el-chat-message--medium')).to.be.true; + }); + + it('type primary', () => { + vm = createTest(ChatMessage, { + ...testProps, + type: 'primary' + }, true); + let messageElm = vm.$el; + expect(messageElm.classList.contains('el-chat-message--primary')).to.be.true; + }); + + it('position right', () => { + vm = createTest(ChatMessage, { + ...testProps, + position: 'right' + }, true); + let messageElm = vm.$el; + expect(messageElm.classList.contains('is-right')).to.be.true; + }); +}); + diff --git a/types/chat-message.d.ts b/types/chat-message.d.ts new file mode 100644 index 000000000..e392a9233 --- /dev/null +++ b/types/chat-message.d.ts @@ -0,0 +1,44 @@ +import {ElementUIComponent, ElementUIComponentSize} from './component' + +/** ChatMessage type */ +export type ChatMessageType = 'primary' | 'success' | 'warning' | 'danger' + +/** ChatMessage status */ +export type ChatMessageStatus = 'none' | 'sending' | 'delivered' | 'read' + +/** ChatMessage position */ +export type ChatMessagePosition = 'left' | 'right' + +/** ChatMessage text */ +export interface ChatMessageText { + text: string, + stamp?: string, + status?: ChatMessageStatus +} + +/** ChatMessage Component */ +export declare class ElChatMessage extends ElementUIComponent { + /** ChatMessage size */ + size: ElementUIComponentSize + + /** ChatMessage status */ + status: ChatMessageStatus + + /** ChatMessage type */ + type: ChatMessageType + + /** ChatMessage position */ + position: ChatMessagePosition + + /** Sender name */ + name: string + + /** Datetime message */ + stamp: string + + /** Array of message */ + text: Array | string + + /** Sender avatar */ + avatar: string +} diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts index 9657a652a..4f218333d 100644 --- a/types/element-ui.d.ts +++ b/types/element-ui.d.ts @@ -88,6 +88,7 @@ import { ElSpinner } from './spinner' import { ElDescriptions } from './descriptions' import { ElDescriptionsItem } from './descriptions-item' import { ElResult } from './result' +import { ElChatMessage } from './chat-message' export interface InstallationOptions { locale: any, @@ -375,4 +376,7 @@ export class Descriptions extends ElDescriptions {} export class DescriptionsItem extends ElDescriptionsItem {} /** Result Component */ -export class Result extends ElResult {} \ No newline at end of file +export class Result extends ElResult {} + +/** ChatMessage Component */ +export class ChatMessage extends ElChatMessage {}