mirror of
https://github.com/wassname/talk.git
synced 2026-07-23 13:10:20 +08:00
Reimplement QuestionBoxBuilder
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
.iconBubble{
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 10px;
|
||||
color: #9E9E9E;
|
||||
font-size: 24px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.iconPerson{
|
||||
z-index: 2;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
position: absolute;
|
||||
font-size: 33px;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import styles from './DefaultQuestionBoxIcon.css';
|
||||
import {Icon} from 'coral-ui';
|
||||
|
||||
const DefaultQuestionBoxIcon = ({className}) => (
|
||||
<div className={cn(styles.root, className)}>
|
||||
<Icon name="chat_bubble" className={cn(styles.iconBubble)} />
|
||||
<Icon name="person" className={cn(styles.iconPerson)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default DefaultQuestionBoxIcon;
|
||||
@@ -0,0 +1,66 @@
|
||||
.qbInfo {
|
||||
top: 0;
|
||||
border: 0;
|
||||
background: #F0F0F0;
|
||||
color: black;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding-left: 0px;
|
||||
margin-bottom: 0px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
z-index: 2;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
position: absolute;
|
||||
font-size: 33px;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.iconBubble{
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 10px;
|
||||
color: #9E9E9E;
|
||||
font-size: 24px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.iconPerson{
|
||||
z-index: 2;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
position: absolute;
|
||||
font-size: 33px;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.qbIconContainer {
|
||||
position: relative;
|
||||
border: 0;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
margin-left: 0px !important;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
min-height: 100%;
|
||||
padding: 5px 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.qbContent {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import styles from './QuestionBox.css';
|
||||
import {Icon} from 'coral-ui';
|
||||
import Markdown from 'coral-framework/components/Markdown';
|
||||
import DefaultQuestionBoxIcon from './DefaultQuestionBoxIcon';
|
||||
|
||||
const QuestionBox = ({content, icon, className, children}) => (
|
||||
<div className={cn(styles.qbInfo, 'questionbox-info', className)}>
|
||||
{
|
||||
icon === 'default' ? (
|
||||
<div className={cn(styles.qbIconContainer)}>
|
||||
<DefaultQuestionBoxIcon />
|
||||
</div>
|
||||
) : (
|
||||
<div className={cn(styles.qbIconContainer)}>
|
||||
<Icon name={icon} className={cn(styles.icon)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className={cn(styles.qbContent, 'questionbox-content')}>
|
||||
<Markdown content={content} />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default QuestionBox;
|
||||
@@ -1,6 +1,6 @@
|
||||
.root {
|
||||
position: relative;
|
||||
margin: 12px 12px 12px 0;
|
||||
margin: 12px 12px 24px 0;
|
||||
}
|
||||
|
||||
.action {
|
||||
@@ -12,16 +12,22 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.description{
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: inline-block;
|
||||
padding: 0px 50px;
|
||||
box-sizing: border-box;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class Configuration extends React.Component {
|
||||
id = uuid();
|
||||
|
||||
render() {
|
||||
const {title, children, className, onCheckbox, checked, ...rest} = this.props;
|
||||
const {title, description, children, className, onCheckbox, checked, ...rest} = this.props;
|
||||
return (
|
||||
<div {...rest} className={cn(styles.root, className)}>
|
||||
{checked !== undefined &&
|
||||
@@ -26,6 +26,7 @@ class Configuration extends React.Component {
|
||||
[styles.content]: checked !== undefined,
|
||||
})}>
|
||||
<label htmlFor={this.id} className={styles.title}>{title}</label>
|
||||
<div className={styles.description}>{description}</div>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
@@ -37,6 +38,7 @@ class Configuration extends React.Component {
|
||||
|
||||
Configuration.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
onCheckbox: PropTypes.func,
|
||||
checked: PropTypes.bool,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
.root {
|
||||
}
|
||||
|
||||
.iconList {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
composes: buttonReset from "coral-framework/styles/reset.css";
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background: #F0F0F0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
color: #252525;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
border: solid 2px #F0F0F0;
|
||||
transition: border 0.3s cubic-bezier(.4,0,.2,1);
|
||||
outline: 0;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.button:focus {
|
||||
border: solid 2px #00c96B;
|
||||
}
|
||||
|
||||
.buttonActive {
|
||||
border: solid 2px #00796B;
|
||||
}
|
||||
|
||||
.defaultIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.questionBox {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import QuestionBox from '../../../components/QuestionBox';
|
||||
import {Icon, Spinner} from 'coral-ui';
|
||||
import DefaultQuestionBoxIcon from '../../../components/DefaultQuestionBoxIcon';
|
||||
import cn from 'classnames';
|
||||
import styles from './QuestionBoxBuilder.css';
|
||||
|
||||
const DefaultIcon = <DefaultQuestionBoxIcon className={styles.defaultIcon} />;
|
||||
|
||||
const icons = [
|
||||
{'default': DefaultIcon},
|
||||
'forum',
|
||||
'build',
|
||||
'format_quote',
|
||||
];
|
||||
|
||||
class QuestionBoxBuilder extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.loadEditor();
|
||||
}
|
||||
|
||||
async loadEditor() {
|
||||
const {default: MarkdownEditor} = await import('coral-framework/components/MarkdownEditor');
|
||||
|
||||
return this.setState({
|
||||
loading : false,
|
||||
MarkdownEditor
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {questionBoxIcon, questionBoxContent, onContentChange, onIconChange} = this.props;
|
||||
const {loading, MarkdownEditor} = this.state;
|
||||
|
||||
if (loading) {
|
||||
return <Spinner/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<h4>Include an Icon</h4>
|
||||
|
||||
<ul className={styles.iconList}>
|
||||
{icons.map((item) => {
|
||||
const name = typeof item === 'object' ? Object.keys(item)[0] : item;
|
||||
const icon = typeof item === 'object' ? item[name] : item;
|
||||
return (
|
||||
<li
|
||||
className={styles.item}
|
||||
key={name}
|
||||
>
|
||||
<button
|
||||
className={cn(
|
||||
styles.button,
|
||||
{[styles.buttonActive]: questionBoxIcon === name},
|
||||
)}
|
||||
onClick={() => onIconChange(name)}
|
||||
>
|
||||
{typeof icon === 'string'
|
||||
? <Icon name={icon} />
|
||||
: icon
|
||||
}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
<QuestionBox
|
||||
className={styles.questionBox}
|
||||
icon={questionBoxIcon}
|
||||
content={questionBoxContent}
|
||||
/>
|
||||
|
||||
<MarkdownEditor
|
||||
value={questionBoxContent}
|
||||
onChange={onContentChange}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default QuestionBoxBuilder;
|
||||
@@ -24,3 +24,6 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.questionBoxContainer {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,24 @@ import t from 'coral-framework/services/i18n';
|
||||
import cn from 'classnames';
|
||||
import styles from './Settings.css';
|
||||
import Configuration from './Configuration';
|
||||
import QuestionBoxBuilder from './QuestionBoxBuilder';
|
||||
|
||||
class Settings extends React.Component {
|
||||
render() {
|
||||
const {settings: {moderation, premodLinksEnable}, toggleModeration, togglePremodLinks} = this.props;
|
||||
const {
|
||||
settings: {
|
||||
moderation,
|
||||
premodLinksEnable,
|
||||
questionBoxEnable,
|
||||
questionBoxContent,
|
||||
questionBoxIcon,
|
||||
},
|
||||
onToggleModeration,
|
||||
onTogglePremodLinks,
|
||||
onToggleQuestionBox,
|
||||
onQuestionBoxIconChange,
|
||||
onQuestionBoxContentChange,
|
||||
} = this.props;
|
||||
const changed = false;
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
@@ -28,16 +42,32 @@ class Settings extends React.Component {
|
||||
<Configuration
|
||||
checked={moderation === 'PRE'}
|
||||
title={t('configure.enable_premod')}
|
||||
onCheckbox={toggleModeration}
|
||||
>
|
||||
{t('configure.enable_premod_description')}
|
||||
</Configuration>
|
||||
description={t('configure.enable_premod_description')}
|
||||
onCheckbox={onToggleModeration}
|
||||
/>
|
||||
<Configuration
|
||||
checked={premodLinksEnable}
|
||||
title={t('configure.enable_premod_links')}
|
||||
onCheckbox={togglePremodLinks}
|
||||
description={t('configure.enable_premod_description')}
|
||||
onCheckbox={onTogglePremodLinks}
|
||||
/>
|
||||
<Configuration
|
||||
checked={questionBoxEnable}
|
||||
title={t('configure.enable_questionbox')}
|
||||
description={t('configure.enable_questionbox_description')}
|
||||
onCheckbox={onToggleQuestionBox}
|
||||
>
|
||||
{t('configure.enable_premod_description')}
|
||||
{
|
||||
questionBoxEnable &&
|
||||
<div className={styles.questionBoxContainer}>
|
||||
<QuestionBoxBuilder
|
||||
questionBoxIcon={questionBoxIcon}
|
||||
questionBoxContent={questionBoxContent}
|
||||
onIconChange={onQuestionBoxIconChange}
|
||||
onContentChange={onQuestionBoxContentChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</Configuration>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,8 +77,9 @@ class Settings extends React.Component {
|
||||
|
||||
Settings.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
toggleModeration: PropTypes.func.isRequired,
|
||||
togglePremodLinks: PropTypes.func.isRequired,
|
||||
onToggleModeration: PropTypes.func.isRequired,
|
||||
onTogglePremodLinks: PropTypes.func.isRequired,
|
||||
onToggleQuestionBox: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -22,6 +22,21 @@ class SettingsContainer extends React.Component {
|
||||
this.props.updatePending({updater});
|
||||
};
|
||||
|
||||
toggleQuestionBox = () => {
|
||||
const updater = {questionBoxEnable: {$set: !this.props.mergedSettings.questionBoxEnable}};
|
||||
this.props.updatePending({updater});
|
||||
};
|
||||
|
||||
setQuestionBoxIcon = (icon) => {
|
||||
const updater = {questionBoxIcon: {$set: icon}};
|
||||
this.props.updatePending({updater});
|
||||
};
|
||||
|
||||
setQuestionBoxContent = (content) => {
|
||||
const updater = {questionBoxContent: {$set: content}};
|
||||
this.props.updatePending({updater});
|
||||
};
|
||||
|
||||
savePending = async () => {
|
||||
try {
|
||||
await this.props.updateAssetSettings(this.props.asset.id, this.props.pending);
|
||||
@@ -36,8 +51,12 @@ class SettingsContainer extends React.Component {
|
||||
return <Settings
|
||||
settings={this.props.mergedSettings}
|
||||
savePending={this.savePending}
|
||||
toggleModeration={this.toggleModeration}
|
||||
togglePremodLinks={this.togglePremodLinks}
|
||||
onToggleModeration={this.toggleModeration}
|
||||
onTogglePremodLinks={this.togglePremodLinks}
|
||||
onToggleQuestionBox={this.toggleQuestionBox}
|
||||
onQuestionBoxIconChange={this.setQuestionBoxIcon}
|
||||
onQuestionBoxContentChange={this.setQuestionBoxContent}
|
||||
|
||||
/>;
|
||||
}
|
||||
}
|
||||
@@ -59,6 +78,9 @@ const withSettingsFragments = withFragments({
|
||||
settings {
|
||||
moderation
|
||||
premodLinksEnable
|
||||
questionBoxEnable
|
||||
questionBoxIcon
|
||||
questionBoxContent
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -252,7 +252,14 @@ class Stream extends React.Component {
|
||||
content={asset.settings.questionBoxContent}
|
||||
enable={asset.settings.questionBoxEnable}
|
||||
icon={asset.settings.questionBoxIcon}
|
||||
/>
|
||||
>
|
||||
<Slot
|
||||
fill="streamQuestionArea"
|
||||
queryData={slotQueryData}
|
||||
{...slotProps}
|
||||
/>
|
||||
</QuestionBox>
|
||||
|
||||
{!banned &&
|
||||
temporarilySuspended &&
|
||||
<RestrictedMessageBox>
|
||||
|
||||
Reference in New Issue
Block a user