Dark theme (#228)

* add with-theme hoc

* add dark theme styles for components

* add toggle theme button to the dev demo page

* add info about widget themes to README
This commit is contained in:
Igor Adamenko
2018-12-15 17:10:41 -06:00
committed by Umputun
parent 126edb39f0
commit 6f1b9037d5
83 changed files with 626 additions and 105 deletions
+15 -1
View File
@@ -282,7 +282,8 @@ Add this snippet to the bottom of web page:
var remark_config = {
site_id: 'YOUR_SITE_ID',
url: 'PAGE_URL', // optional param; if it isn't defined window.location.href will be used
max_shown_comments: 10, // optional param; if it isn't defined default value (15) will be used
max_shown_comments: 10, // optional param; if it isn't defined default value (15) will be used
theme: 'dark', // optional param; if it isn't defined default value ('light') will be used
};
(function() {
@@ -301,6 +302,19 @@ And then add this node in the place where you want to see Remark42 widget:
After that widget will be rendered inside this node.
##### Themes
Right now Remark has two themes: light and dark.
You can pick one using configuration object,
but there is also a possibility to switch between themes in runtime.
For this purpose Remark adds to `window` object named `REMARK42`,
which contains function `changeTheme`.
Just call this function and pass a name of the theme that you want to turn on:
```js
window.REMARK42.changeTheme('light');
```
#### Last comments
It's a widget which renders list of last comments from your site.
+2
View File
@@ -39,6 +39,8 @@ export const BLOCKING_DURATIONS = [
},
];
export const THEMES = ['light', 'dark'];
/**
* Defines if browser storage features (cookies, localsrotage)
* are available or blocked via browser preferences
+3
View File
@@ -1,3 +1,5 @@
import { THEMES } from './constants';
const querySettings =
window.location.search
.substr(1)
@@ -12,3 +14,4 @@ export const siteId = querySettings['site_id'];
export const url = querySettings['url'];
export const maxShownComments = querySettings['max_shown_comments'];
export const token = querySettings['token'];
export const theme = querySettings['theme'] || THEMES[0];
@@ -1,8 +1,8 @@
/** @jsx h */
import { h } from 'preact';
export default ({ id }) => (
<div className="auth-panel__user-id" title={id}>
{id}
export default props => (
<div className={b('auth-panel__user-id', props)} title={props.id}>
{props.id}
</div>
);
@@ -1,5 +1,4 @@
.auth-panel__user-id {
overflow: hidden;
text-overflow: ellipsis;
color: #888;
}
@@ -0,0 +1,6 @@
import withTheme from 'components/with-theme';
import UserId from './auth-panel__user-id';
export default withTheme(UserId);
require('./auth-panel__user-id.scss');
@@ -0,0 +1,5 @@
.auth-panel_theme_dark {
.auth-panel__user-id {
color: #eee;
}
}
@@ -0,0 +1,5 @@
.auth-panel_theme_light {
.auth-panel__user-id {
color: #888;
}
}
+2 -1
View File
@@ -1,13 +1,14 @@
/** @jsx h */
import { h, Component } from 'preact';
import UserId from './__user-id/auth-panel__user-id';
import Dropdown, { DropdownItem } from 'components/dropdown';
import Button from 'components/button';
import { PROVIDER_NAMES, IS_STORAGE_AVAILABLE, IS_THIRD_PARTY } from 'common/constants';
import { requestDeletion } from 'utils/email';
import { getHandleClickProps } from 'common/accessibility';
import UserId from './__user-id';
export default class AuthPanel extends Component {
constructor(props) {
super(props);
+7 -1
View File
@@ -1,4 +1,7 @@
export { default } from './auth-panel';
import withTheme from 'components/with-theme';
import AuthPanel from './auth-panel';
export default withTheme(AuthPanel);
require('./auth-panel.scss');
@@ -11,4 +14,7 @@ require('./__sort/auth-panel__sort.scss');
require('./__user-id/auth-panel__user-id.scss');
require('./__sign-out/auth-panel__sign-out.scss');
require('./_theme/_dark/auth-panel_theme_dark.scss');
require('./_theme/_light/auth-panel_theme_light.scss');
require('./_logged-in/auth-panel_logged-in.scss');
@@ -1,10 +1,12 @@
/** @jsx h */
import { h } from 'preact';
export default function AvatarIcon({ picture, className }) {
export default function AvatarIcon(props) {
const { picture } = props;
return (
<img
className={b('avatar-icon', { mix: className }, { default: !picture })}
className={b('avatar-icon', props, { default: !picture })}
src={picture || require('./avatar-icon.svg')}
alt=""
/>
+4 -1
View File
@@ -1,4 +1,7 @@
export { default } from './avatar-icon';
import withTheme from 'components/with-theme';
import AvatarIcon from './avatar-icon';
export default withTheme(AvatarIcon);
require('./avatar-icon.scss');
require('./_default/avatar-icon_default.scss');
@@ -15,6 +15,5 @@
&::before {
content: '';
margin-right: 8px;
color: #777;
}
}
@@ -1,3 +0,0 @@
.blocked-users__user-id {
color: #888;
}
@@ -1,3 +0,0 @@
.blocked-users__user-id {
color: #888;
}
@@ -0,0 +1,11 @@
.blocked-users_theme_dark {
.blocked-users__action {
&::before {
color: #ddd;
}
}
.blocked-users__user-id {
color: #eee;
}
}
@@ -0,0 +1,11 @@
.blocked-users_theme_light {
.blocked-users__action {
&::before {
color: #777;
}
}
.blocked-users__user-id {
color: #888;
}
}
+7 -3
View File
@@ -1,4 +1,7 @@
export { default } from './blocked-users';
import withTheme from 'components/with-theme';
import BlockedUsers from './blocked-users';
export default withTheme(BlockedUsers);
require('./blocked-users.scss');
@@ -6,8 +9,9 @@ require('./__action/blocked-users__action.scss');
require('./__list/blocked-users__list.scss');
require('./__list-item/blocked-users__list-item.scss');
require('./__user-block-ttl/blocked-users__user-block-ttl.scss');
require('./__list-item/_view/_invisible/blocked-users__list-item_view_invisible.scss');
require('./__user-id/blocked-users__user-id.scss');
require('./__username/blocked-users__username.scss');
require('./_theme/_dark/blocked-users_theme_dark.scss');
require('./_theme/_light/blocked-users_theme_light.scss');
@@ -1,9 +1,3 @@
.button_kind_text {
border: none;
background: transparent;
padding: 0;
}
.button_kind_link {
border: none;
background: transparent;
@@ -0,0 +1,5 @@
.button_kind_text {
border: none;
background: transparent;
padding: 0;
}
+8 -3
View File
@@ -44,13 +44,18 @@ export default class Button extends Component {
}
render(props, state) {
const { children, mix, mods, ...rest } = props;
const { children } = props;
const { isClicked, isFocused } = state;
const localProps = { ...props };
delete localProps.children;
delete localProps.mix;
delete localProps.mods;
return (
<button
{...rest}
className={b('button', { mix, mods }, { clicked: isClicked, focused: isFocused })}
{...localProps}
className={b('button', props, { clicked: isClicked, focused: isFocused })}
onMouseDown={this.onMouseDown}
onBlur={this.onBlur}
onFocus={this.onFocus}
+8 -2
View File
@@ -1,5 +1,11 @@
export { default } from './button';
import withTheme from 'components/with-theme';
import Button from './button';
export default withTheme(Button);
require('./button.scss');
require('./_kind/button_kind.scss');
require('./_kind/_link/button_kind_link.scss');
require('./_kind/_text/button_kind_text.scss');
require('./_focused/button_focused.scss');
@@ -9,9 +9,8 @@
font-size: 12px;
line-height: 10px;
text-align: center;
border: 1px solid #ddd;
border: 1px solid;
border-radius: 2px;
color: #ddd;
&:hover {
border-color: #0aa;
@@ -19,9 +18,6 @@
}
&.comment__action_selected {
background: #ddd;
color: #fff;
&:hover {
background: #0aa;
}
@@ -1,6 +1,4 @@
.comment__action_type_delete {
color: #a6a6a6;
&:hover {
color: #31c7c5;
}
@@ -1,6 +1,4 @@
.comment__action_type_edit {
color: #a6a6a6;
&:hover {
color: #31c7c5;
}
@@ -18,7 +18,6 @@
content: '';
margin-left: 8px;
margin-right: 8px;
color: #777;
}
}
}
@@ -2,6 +2,7 @@
position: relative;
white-space: nowrap;
font-weight: 700;
&::after {
content: '';
margin-left: 2px;
@@ -2,7 +2,6 @@
&,
&:hover,
&:focus {
color: #999;
pointer-events: none;
}
}
@@ -2,7 +2,6 @@
font-size: 14px;
vertical-align: middle;
user-select: none;
color: #a6a6a6;
margin-left: 8px;
+ .comment__controls {
@@ -10,7 +9,6 @@
content: '';
margin-left: 8px;
margin-right: 8px;
color: #777;
}
}
}
@@ -6,7 +6,6 @@
font-size: 0;
font-weight: 700;
text-align: right;
color: #c4c4c4;
user-select: none;
cursor: default;
}
@@ -2,5 +2,4 @@
margin-left: 8px;
vertical-align: middle;
font-weight: 700;
color: #999;
}
@@ -2,7 +2,6 @@
margin-left: 8px;
margin-right: 8px;
vertical-align: middle;
color: #999;
text-decoration: none;
&:hover {
@@ -1,4 +1,3 @@
.comment__user-id {
color: #888;
font-weight: 400;
}
@@ -2,7 +2,6 @@
vertical-align: middle;
font-weight: 700;
text-decoration: none;
color: #777;
cursor: pointer;
&:hover {
@@ -4,7 +4,7 @@
}
.comment__input {
border: 12px solid #eee;
border: 12px solid;
}
.comment__score {
@@ -14,7 +14,7 @@
/* it isn't mobile first, but it's fine here */
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border: 8px solid #eee;
border: 8px solid;
padding-bottom: 0;
.comment__body {
@@ -1,6 +1,6 @@
.comment_replying {
.comment__input {
border: 12px solid #eee;
border: 12px solid;
}
.comment__score {
@@ -10,7 +10,7 @@
/* it isn't mobile first, but it's fine here */
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border: 8px solid #eee;
border: 8px solid;
padding-bottom: 0;
.comment__body {
@@ -0,0 +1,94 @@
.comment_theme_dark {
border-bottom: 1px solid #333;
.comment__action_type_collapse {
border-color: #555;
color: #555;
&.comment__action_selected {
background: #555;
color: #22201c;
}
}
.comment__action_type_delete {
color: #6a6a6a;
}
.comment__action_type_edit {
color: #6a6a6a;
}
.comment__action {
+ .comment__controls {
&::before {
color: #eee;
}
}
}
.comment__control_view_inactive {
&,
&:hover,
&:focus {
color: #999;
}
}
.comment__edit-timer {
color: #a6a6a6;
+ .comment__controls {
&::before {
color: #eee;
}
}
}
.comment__score {
color: #c4c4c4;
}
.comment__status {
color: #999;
}
.comment__time {
color: #999;
}
.comment__user-id {
color: #888;
}
.comment__username {
color: #777;
}
&.comment_editing {
.comment__input {
border-color: #333;
}
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border-color: #333;
}
}
&.comment_replying {
.comment__input {
border-color: #333;
}
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border-color: #333;
}
}
&.comment_view_preview {
.comment__info {
&::after {
color: #777;
}
}
}
}
@@ -0,0 +1,94 @@
.comment_theme_light {
border-bottom: 1px solid #fcfcfc;
.comment__action_type_collapse {
border-color: #ddd;
color: #ddd;
&.comment__action_selected {
background: #ddd;
color: #fff;
}
}
.comment__action_type_delete {
color: #a6a6a6;
}
.comment__action_type_edit {
color: #a6a6a6;
}
.comment__action {
+ .comment__controls {
&::before {
color: #777;
}
}
}
.comment__control_view_inactive {
&,
&:hover,
&:focus {
color: #999;
}
}
.comment__edit-timer {
color: #a6a6a6;
+ .comment__controls {
&::before {
color: #777;
}
}
}
.comment__score {
color: #c4c4c4;
}
.comment__status {
color: #999;
}
.comment__time {
color: #999;
}
.comment__user-id {
color: #888;
}
.comment__username {
color: #777;
}
&.comment_editing {
.comment__input {
border-color: #eee;
}
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border-color: #eee;
}
}
&.comment_replying {
.comment__input {
border-color: #eee;
}
@media (-moz-touch-enabled: 1) and (max-width: 768px), (pointer: coarse) and (max-width: 768px) {
border-color: #eee;
}
}
&.comment_view_preview {
.comment__info {
&::after {
color: #777;
}
}
}
}
@@ -1,6 +1,7 @@
.comment_useless {
.comment__body {
opacity: 0.35;
&:hover {
opacity: 1;
}
+5 -2
View File
@@ -410,7 +410,10 @@ export default class Comment extends Component {
{o.user.name}
</a>
</div>{' '}
<div className={b('comment__text', { mix: 'raw-content' })} dangerouslySetInnerHTML={{ __html: o.text }} />
<div
className={b('comment__text', { mix: b('raw-content', {}, { theme: mods.theme }) })}
dangerouslySetInnerHTML={{ __html: o.text }}
/>
</div>
</article>
);
@@ -529,7 +532,7 @@ export default class Comment extends Component {
</div>
<div
className={b('comment__text', { mix: 'raw-content' })}
className={b('comment__text', { mix: b('raw-content', {}, { theme: mods.theme }) })}
ref={r => (this.textNode = r)}
dangerouslySetInnerHTML={{ __html: o.text }}
/>
-1
View File
@@ -4,7 +4,6 @@
overflow: hidden;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #fcfcfc;
font-size: 16px;
line-height: 1.2;
+6 -1
View File
@@ -1,6 +1,8 @@
import 'components/raw-content';
import withTheme from 'components/with-theme';
import Comment from './comment';
export { default } from './comment';
export default withTheme(Comment);
require('./comment.scss');
@@ -51,3 +53,6 @@ require('./_useless/comment_useless.scss');
require('./_view/_admin/comment_view_admin.scss');
require('./_view/_preview/comment_view_preview.scss');
require('./_view/_user/comment_view_user.scss');
require('./_theme/_dark/comment_theme_dark.scss');
require('./_theme/_light/comment_theme_light.scss');
@@ -7,7 +7,6 @@
left: 50%;
transform: translate(-50%, 5px);
width: 170px;
background-color: #fff;
border: 2px solid #259c9a;
border-radius: 3px;
padding: 0 0 5px;
@@ -2,7 +2,7 @@
import { h } from 'preact';
export default function DropdownItem(props) {
const { children, separator = false, mix, mods } = props;
const { children, separator = false } = props;
return <div className={b('dropdown__item', { mix, mods }, { separator })}>{children}</div>;
return <div className={b('dropdown__item', props, { separator })}>{children}</div>;
}
@@ -0,0 +1,4 @@
import withTheme from 'components/with-theme';
import Dropdown__Item from './dropdown__item';
export default withTheme(Dropdown__Item);
@@ -0,0 +1,9 @@
.dropdown_theme_dark {
.dropdown__title {
color: #ddd;
}
.dropdown__content {
background-color: #22201c;
}
}
@@ -0,0 +1,9 @@
.dropdown_theme_light {
.dropdown__title {
color: #000;
}
.dropdown__content {
background-color: #fff;
}
}
+9 -2
View File
@@ -1,5 +1,9 @@
export { default } from './dropdown';
export { default as DropdownItem } from './__item/dropdown__item';
import withTheme from 'components/with-theme';
import Dropdown from './dropdown';
export default withTheme(Dropdown);
export { default as DropdownItem } from './__item';
require('./dropdown.scss');
require('./_active/dropdown_active.scss');
@@ -8,3 +12,6 @@ require('./__item/dropdown__item.scss');
require('./__items/dropdown__items.scss');
require('./__title/dropdown__title.scss');
require('./__content/dropdown__content.scss');
require('./_theme/_dark/dropdown_theme_dark.scss');
require('./_theme/_light/dropdown_theme_light.scss');
@@ -2,5 +2,4 @@
display: flex;
align-items: center;
padding-top: 8px;
background: #eee;
}
@@ -1,7 +1,4 @@
.input__button_type_preview {
background: #fff;
color: #000;
&:hover,
&:focus {
box-shadow: inset 0 0 0 2px #0aa;
@@ -3,7 +3,4 @@
padding: 10px 12px;
font-size: 14px;
line-height: 18px;
border-top: 8px solid #eee;
background: #ffd7d7;
color: #9a0000;
}
@@ -14,7 +14,6 @@
font-family: 'PT Sans', Helvetica, Arial, sans-serif;
font-size: $fontSize;
line-height: $lineHeight;
background: #fff;
border: 0;
resize: none;
backface-visibility: hidden; /* let's try to fix blinking in Safari */
@@ -1,4 +1,3 @@
.input__preview-wrapper {
overflow: hidden;
background: #eee;
}
@@ -4,8 +4,6 @@
overflow: hidden;
font-size: 16px;
line-height: 1.2;
border: 1px dashed #eee;
border: 1px dashed;
border-radius: 2px;
background: #fff;
color: #333;
}
@@ -0,0 +1,38 @@
.input_theme_dark {
border-color: #333;
background: #22201c; /* try to fix textarea blinking in Safari */
.input__actions {
background: #333;
}
.input__button_type_preview {
background: #22201c;
color: #ddd;
}
.input__button_type_send {
color: #ddd;
}
.input__error {
border-top: 8px solid #333;
background: #672323;
color: #f98989;
}
.input__field {
background: #22201c;
color: #eee;
}
.input__preview {
border-color: #eee;
background: #fff;
color: #333;
}
.input__preview-wrapper {
background: #eee;
}
}
@@ -0,0 +1,38 @@
.input_theme_light {
border-color: #eee;
background: #fff; /* try to fix textarea blinking in Safari */
.input__actions {
background: #eee;
}
.input__button_type_preview {
background: #fff;
color: #000;
}
.input__button_type_send {
color: #fff;
}
.input__error {
border-top: 8px solid #eee;
background: #ffd7d7;
color: #9a0000;
}
.input__field {
background: #fff;
color: #000;
}
.input__preview {
border-color: #eee;
background: #fff;
color: #333;
}
.input__preview-wrapper {
background: #eee;
}
}
+6 -1
View File
@@ -1,6 +1,8 @@
import 'components/raw-content';
import withTheme from 'components/with-theme';
import Input from './input';
export { default } from './input';
export default withTheme(Input);
require('./input.scss');
@@ -20,3 +22,6 @@ require('./__rss/input__rss.scss');
require('./__rss-link/input__rss-link.scss');
require('./__markdown/input__markdown.scss');
require('./__markdown-link/input__markdown-link.scss');
require('./_theme/_dark/input_theme_dark.scss');
require('./_theme/_light/input_theme_light.scss');
+2 -1
View File
@@ -40,6 +40,7 @@ export default class Input extends Component {
shouldComponentUpdate(nextProps, nextState) {
return (
nextProps.id !== this.props.id ||
nextProps.mods !== this.props.mods ||
nextProps.pid !== this.props.pid ||
nextProps.value !== this.props.value ||
nextProps.errorMessage !== this.props.errorMessage ||
@@ -171,7 +172,7 @@ export default class Input extends Component {
!!preview && (
<div className="input__preview-wrapper">
<div
className={b('input__preview', { mix: 'raw-content' })}
className={b('input__preview', { mix: b('raw-content', {}, { theme: mods.theme }) })}
dangerouslySetInnerHTML={{ __html: preview }}
/>
</div>
+1 -2
View File
@@ -2,7 +2,6 @@
position: relative;
display: block;
font-size: 0;
border: 12px solid #eee;
border: 12px solid;
border-radius: 2px;
background: #fff; /* try to fix textarea blinking in Safari */
}
@@ -0,0 +1,28 @@
.raw-content_theme_dark {
blockquote {
background: #393734;
}
table {
th,
td {
border: 1px solid #313133;
}
tr {
background-color: #22201c;
&:nth-child(2n) {
background-color: #393734;
}
}
}
pre {
background-color: #393734;
}
hr {
border-color: #393734;
}
}
@@ -0,0 +1,28 @@
.raw-content_theme_light {
blockquote {
background: #fafafa;
}
table {
th,
td {
border: 1px solid #dfe2e5;
}
tr {
background-color: #fff;
&:nth-child(2n) {
background-color: #f6f8fa;
}
}
}
pre {
background-color: #f6f8fa;
}
hr {
border-color: #f6f8fa;
}
}
+3
View File
@@ -1 +1,4 @@
require('./raw-content.scss');
require('./_theme/_dark/raw-content_theme_dark.scss');
require('./_theme/_light/raw-content_theme_light.scss');
@@ -44,7 +44,6 @@
blockquote {
margin: 0;
padding: 8px 8px 8px 24px;
background: #fafafa;
border-radius: 2px;
+ p {
@@ -66,16 +65,10 @@
th,
td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}
tr {
background-color: #fff;
border-top: 1px solid #b7dddd;
&:nth-child(2n) {
background-color: #f6f8fa;
}
}
}
@@ -95,7 +88,6 @@
word-wrap: normal;
font-size: 85%;
line-height: 1.45;
background-color: #f6f8fa;
border-radius: 3px;
&:first-child {
@@ -1,3 +0,0 @@
.root__copyright-link {
color: #aaa;
}
@@ -2,5 +2,4 @@
margin: 48px 0 0;
font-size: 12px;
text-align: right;
color: #aaa;
}
@@ -1,6 +1,4 @@
.root__pinned-comment {
border-bottom-color: #e2efef;
&:last-child {
margin-bottom: 0;
border-bottom: 0;
@@ -2,5 +2,4 @@
margin-top: 24px;
padding: 8px 12px;
border-radius: 2px;
background: #edf6f7;
}
@@ -0,0 +1,19 @@
.root_theme_dark {
color: #ddd;
.root__copyright {
color: #aaa;
}
.root__copyright-link {
color: #aaa;
}
.root__pinned-comment {
border-bottom-color: #383838;
}
.root__pinned-comments {
background: #404040;
}
}
@@ -0,0 +1,19 @@
.root_theme_light {
color: #000;
.root__copyright {
color: #aaa;
}
.root__copyright-link {
color: #aaa;
}
.root__pinned-comment {
border-bottom-color: #e2efef;
}
.root__pinned-comments {
background: #edf6f7;
}
}
+3 -1
View File
@@ -3,7 +3,6 @@ export { default } from './root';
require('./root.scss');
require('./__copyright/root__copyright.scss');
require('./__copyright-link/root__copyright-link.scss');
require('./__input/root__input.scss');
require('./__preloader/root__preloader.scss');
require('./__pinned-comment/root__pinned-comment.scss');
@@ -11,3 +10,6 @@ require('./__pinned-comments/root__pinned-comments.scss');
require('./__show-more/root__show-more.scss');
require('./__thread/root__thread.scss');
require('./__threads/root__threads.scss');
require('./_theme/_dark/root_theme_dark.scss');
require('./_theme/_light/root_theme_light.scss');
+27 -3
View File
@@ -9,9 +9,10 @@ import {
DEFAULT_SORT,
COOKIE_SORT_KEY,
MAX_SHOWN_ROOT_COMMENTS,
THEMES,
} from 'common/constants';
import { getCookie, setCookie } from 'common/cookies';
import { siteId, url, maxShownComments } from 'common/settings';
import { siteId, url, maxShownComments, theme } from 'common/settings';
import store from 'common/store';
import AuthPanel from 'components/auth-panel';
@@ -39,6 +40,7 @@ export default class Root extends Component {
isLoaded: false,
isCommentsListLoading: false,
user: {},
theme: THEMES[0],
sort,
commentsShown: maxShownComments || MAX_SHOWN_ROOT_COMMENTS,
};
@@ -55,11 +57,22 @@ export default class Root extends Component {
this.onUnblockSomeone = this.onUnblockSomeone.bind(this);
this.checkUrlHash = this.checkUrlHash.bind(this);
this.showMore = this.showMore.bind(this);
this.onThemeUpdate = this.onThemeUpdate.bind(this);
}
componentWillMount() {
store.onUpdate('comments', comments => this.setState({ comments }));
store.onUpdate('info', info => this.setState({ info }));
if (THEMES.includes(theme)) {
store.set('theme', theme);
this.setState({ theme });
} else {
store.set('theme', THEMES[0]);
this.setState({ theme: THEMES[0] });
}
window.addEventListener('message', this.onThemeUpdate);
}
componentDidMount() {
@@ -109,6 +122,16 @@ export default class Root extends Component {
}
}
onThemeUpdate(event) {
try {
const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
if (data.theme && THEMES.includes(data.theme)) {
store.set('theme', data.theme);
this.setState({ theme: data.theme });
}
} catch (e) {}
}
onSignOut() {
api.logOut().then(() => {
store.set('user', {});
@@ -238,12 +261,13 @@ export default class Root extends Component {
isCommentsListLoading,
bannedUsers,
commentsShown,
theme,
}
) {
if (!isLoaded) {
return (
<div id={NODE_ID}>
<div className="root">
<div className={b('root', props, { theme })}>
<Preloader mix="root__preloader" />
</div>
</div>
@@ -257,7 +281,7 @@ export default class Root extends Component {
return (
<div id={NODE_ID}>
<div className="root">
<div className={b('root', props, { theme })}>
<AuthPanel
user={user}
sort={sort}
@@ -3,6 +3,5 @@
font-size: 14px;
font-weight: 400;
line-height: 18px;
color: #888;
padding-left: 40px;
}
@@ -0,0 +1,10 @@
.user-info_theme_light {
.user-info {
border-color: #efefef;
background: #fff;
}
.user-info__id {
color: #888;
}
}
+2
View File
@@ -9,3 +9,5 @@ require('./__id/user-info__id.scss');
require('./__preloader/user-info__preloader.scss');
require('./__title/user-info__title.scss');
require('./__avatar/user-info__avatar.scss');
require('./_theme/_light/user-info_theme_light.scss');
+1 -1
View File
@@ -51,7 +51,7 @@ class UserInfo extends Component {
return (
<div className={b('user-info', props)}>
<Avatar className="user-info__avatar" picture={isDefaultPicture ? null : picture} />
<Avatar mix="user-info__avatar" picture={isDefaultPicture ? null : picture} />
<p className="user-info__title">Last comments by {name}</p>
<p className="user-info__id">{id}</p>
+1 -2
View File
@@ -1,12 +1,11 @@
.user-info {
border: 1px solid #efefef;
border: 1px solid;
border-radius: 2px;
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 100%;
background: white;
padding: 10px;
box-sizing: border-box;
+1
View File
@@ -0,0 +1 @@
export { default } from './with-theme';
@@ -0,0 +1,16 @@
/** @jsx h */
import { h } from 'preact';
import store from 'common/store';
const withTheme = PlainComponent => {
const ThemedComponent = props => {
const { mods = {} } = props;
const theme = store.get('theme');
return <PlainComponent {...props} mods={{ theme, ...mods }} />;
};
return ThemedComponent;
};
export default withTheme;
+7
View File
@@ -29,6 +29,9 @@ function init() {
remark_config.url = (remark_config.url || window.location.href).split('#')[0];
window.REMARK42 = window.REMARK42 || {};
window.REMARK42.changeTheme = changeTheme;
const query = Object.keys(remark_config)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(remark_config[key])}`)
.join('&');
@@ -244,4 +247,8 @@ function init() {
iframe.contentWindow.postMessage(JSON.stringify({ clickOutside: true }), '*');
}
}
function changeTheme(theme) {
iframe.contentWindow.postMessage(JSON.stringify({ theme }), '*');
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="ru">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
+1 -1
View File
@@ -34,7 +34,7 @@
width: 10px;
height: 10px;
margin-right: 3px;
background-color: #333;
background-color: #888;
border-radius: 100%;
animation: iframePreloaderBounce 1.4s infinite ease-in-out both;
}
+27 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="ru">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
@@ -15,7 +15,32 @@
max-width: 800px;
margin: 40px;
}
.document_theme_dark {
background: #22201c;
color: #ddd;
}
.document_theme_dark a {
color: #5e5eff;
}
</style>
<script>
function toggleTheme() {
var body = document.querySelector('body');
if (body.classList.contains('document_theme_dark')) {
body.classList.add('document_theme_light');
body.classList.remove('document_theme_dark');
window.REMARK42.changeTheme('light');
} else {
body.classList.add('document_theme_dark');
body.classList.remove('document_theme_light');
window.REMARK42.changeTheme('dark');
}
}
</script>
</head>
<body>
<div class="container">
@@ -26,6 +51,7 @@
<li><a href="/web/last-comments.html">Last comments widget</a></li>
<li><a href="/web/counter.html">Counter widget</a></li>
</ul>
<p><Button onclick="toggleTheme()">Toggle theme</Button></p>
<div id="remark42"></div>
</div>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="ru">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">