Просмотр исходного кода

Merge pull request #2925 from weseek/imprv/editmode-slack-button

Imprv/editmode slack button
Yuki Takei 5 лет назад
Родитель
Сommit
267cc0fe44

+ 6 - 0
public/images/icons/slack/slack-logo-dark-off.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 448">
+  <defs>
+    <style>.cls-1{fill:#9BA5AF;}</style>
+  </defs>
+  <path class="cls-1" d="M94.12,283.1A47.06,47.06,0,1,1,47.06,236H94.12Zm23.72,0a47.06,47.06,0,1,1,94.12,0V400.94a47.06,47.06,0,1,1-94.12,0Zm47.06-189A47.06,47.06,0,1,1,212,47.06V94.12Zm0,23.72a47.06,47.06,0,0,1,0,94.12H47.06a47.06,47.06,0,0,1,0-94.12Zm189,47.06A47.06,47.06,0,1,1,400.94,212H353.88V164.9Zm-23.72,0a47.06,47.06,0,1,1-94.12,0V47.06a47.06,47.06,0,1,1,94.12,0V164.9Zm-47.06,189A47.06,47.06,0,1,1,236,400.94V353.88Zm0-23.72a47.06,47.06,0,0,1,0-94.12H400.94a47.06,47.06,0,0,1,0,94.12Z"/>
+</svg>

+ 6 - 0
public/images/icons/slack/slack-logo-dark-on.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 448">
+  <defs>
+    <style>.cls-1{fill:#DD80DE;}</style>
+  </defs>
+  <path class="cls-1" d="M94.12,283.1A47.06,47.06,0,1,1,47.06,236H94.12Zm23.72,0a47.06,47.06,0,1,1,94.12,0V400.94a47.06,47.06,0,1,1-94.12,0Zm47.06-189A47.06,47.06,0,1,1,212,47.06V94.12Zm0,23.72a47.06,47.06,0,0,1,0,94.12H47.06a47.06,47.06,0,0,1,0-94.12Zm189,47.06A47.06,47.06,0,1,1,400.94,212H353.88V164.9Zm-23.72,0a47.06,47.06,0,1,1-94.12,0V47.06a47.06,47.06,0,1,1,94.12,0V164.9Zm-47.06,189A47.06,47.06,0,1,1,236,400.94V353.88Zm0-23.72a47.06,47.06,0,0,1,0-94.12H400.94a47.06,47.06,0,0,1,0,94.12Z"/>
+</svg>

+ 6 - 0
public/images/icons/slack/slack-logo-off.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 448">
+  <defs>
+    <style>.cls-1{fill:#9ba5af;}</style>
+  </defs>
+  <path class="cls-1" d="M94.12,283.1A47.06,47.06,0,1,1,47.06,236H94.12Zm23.72,0a47.06,47.06,0,1,1,94.12,0V400.94a47.06,47.06,0,1,1-94.12,0Zm47.06-189A47.06,47.06,0,1,1,212,47.06V94.12Zm0,23.72a47.06,47.06,0,0,1,0,94.12H47.06a47.06,47.06,0,0,1,0-94.12Zm189,47.06A47.06,47.06,0,1,1,400.94,212H353.88V164.9Zm-23.72,0a47.06,47.06,0,1,1-94.12,0V47.06a47.06,47.06,0,1,1,94.12,0V164.9Zm-47.06,189A47.06,47.06,0,1,1,236,400.94V353.88Zm0-23.72a47.06,47.06,0,0,1,0-94.12H400.94a47.06,47.06,0,0,1,0,94.12Z"/>
+</svg>

+ 6 - 0
public/images/icons/slack/slack-logo-on.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 448">
+  <defs>
+    <style>.cls-1{fill:#af30b0;}</style>
+  </defs>
+  <path class="cls-1" d="M94.12,283.1A47.06,47.06,0,1,1,47.06,236H94.12Zm23.72,0a47.06,47.06,0,1,1,94.12,0V400.94a47.06,47.06,0,1,1-94.12,0Zm47.06-189A47.06,47.06,0,1,1,212,47.06V94.12Zm0,23.72a47.06,47.06,0,0,1,0,94.12H47.06a47.06,47.06,0,0,1,0-94.12Zm189,47.06A47.06,47.06,0,1,1,400.94,212H353.88V164.9Zm-23.72,0a47.06,47.06,0,1,1-94.12,0V47.06a47.06,47.06,0,1,1,94.12,0V164.9Zm-47.06,189A47.06,47.06,0,1,1,236,400.94V353.88Zm0-23.72a47.06,47.06,0,0,1,0-94.12H400.94a47.06,47.06,0,0,1,0,94.12Z"/>
+</svg>

+ 2 - 0
src/client/js/components/PageComment/CommentEditor.jsx

@@ -346,6 +346,7 @@ class CommentEditor extends React.Component {
             </label>
             <span className="flex-grow-1" />
             <span className="d-none d-sm-inline">{ this.state.errorMessage && errorMessage }</span>
+
             { this.state.hasSlackConfig
               && (
               <div className="form-inline align-self-center mr-md-2">
@@ -354,6 +355,7 @@ class CommentEditor extends React.Component {
                   slackChannels={commentContainer.state.slackChannels}
                   onEnabledFlagChange={this.onSlackEnabledFlagChange}
                   onChannelChange={this.onSlackChannelsChange}
+                  id="idForComment"
                 />
               </div>
               )

+ 59 - 2
src/client/js/components/PageEditor/EditorNavbarBottom.jsx

@@ -1,9 +1,13 @@
 import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 
-import { Collapse } from 'reactstrap';
+import { Collapse, Button } from 'reactstrap';
 
 import NavigationContainer from '../../services/NavigationContainer';
+import EditorContainer from '../../services/EditorContainer';
+import AppContainer from '../../services/AppContainer';
+import SlackNotification from '../SlackNotification';
+import SlackLogo from '../SlackLogo';
 import { withUnstatedContainers } from '../UnstatedUtils';
 
 import SavePageControls from '../SavePageControls';
@@ -14,6 +18,9 @@ const EditorNavbarBottom = (props) => {
 
   const [isExpanded, setExpanded] = useState(false);
 
+  const [isSlackExpanded, setSlackExpanded] = useState(false);
+  const hasSlackConfig = props.appContainer.getConfig().hasSlackConfig;
+
   const {
     navigationContainer,
   } = props;
@@ -27,6 +34,14 @@ const EditorNavbarBottom = (props) => {
     </button>
   );
 
+  const slackEnabledFlagChangedHandler = (isSlackEnabled) => {
+    props.editorContainer.setState({ isSlackEnabled });
+  };
+
+  const slackChannelsChangedHandler = (slackChannels) => {
+    props.editorContainer.setState({ slackChannels });
+  };
+
   // eslint-disable-next-line react/prop-types
   const renderExpandButton = () => (
     <div className="d-md-none ml-2">
@@ -45,12 +60,52 @@ const EditorNavbarBottom = (props) => {
 
   return (
     <div className={`${isCollapsedOptionsSelectorEnabled ? 'fixed-bottom' : ''} `}>
+      {/* Collapsed SlackNotification */}
+      {hasSlackConfig && (
+        <Collapse isOpen={isSlackExpanded && isDeviceSmallerThanMd}>
+          <nav className={`navbar navbar-expand-lg border-top ${additionalClasses.join(' ')}`}>
+            <SlackNotification
+              isSlackEnabled={props.editorContainer.state.isSlackEnabled}
+              slackChannels={props.editorContainer.state.slackChannels}
+              onEnabledFlagChange={slackEnabledFlagChangedHandler}
+              onChannelChange={slackChannelsChangedHandler}
+              id="idForEditorNavbarBottomForMobile"
+              popUp
+            />
+          </nav>
+        </Collapse>
+        )
+      }
       <div className={`navbar navbar-expand border-top px-2 ${additionalClasses.join(' ')}`}>
         <form className="form-inline">
           { isDrawerMode && renderDrawerButton() }
           { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }
         </form>
         <form className="form-inline ml-auto">
+          {/* Responsive Design for the SlackNotification */}
+          {/* Button or the normal Slack banner */}
+          {hasSlackConfig && (isDeviceSmallerThanMd ? (
+            <Button
+              className="grw-btn-slack border mr-2"
+              onClick={() => (setSlackExpanded(!isSlackExpanded))}
+            >
+              <div className="grw-slack-logo">
+                <SlackLogo />
+                <span className="grw-btn-slack-triangle fa fa-caret-up ml-2"></span>
+              </div>
+            </Button>
+          ) : (
+            <div className="mr-2">
+              <SlackNotification
+                isSlackEnabled={props.editorContainer.state.isSlackEnabled}
+                slackChannels={props.editorContainer.state.slackChannels}
+                onEnabledFlagChange={slackEnabledFlagChangedHandler}
+                onChannelChange={slackChannelsChangedHandler}
+                id="idForEditorNavbarBottom"
+                popUp={false}
+              />
+            </div>
+          ))}
           <SavePageControls />
           { isCollapsedOptionsSelectorEnabled && renderExpandButton() }
         </form>
@@ -73,6 +128,8 @@ const EditorNavbarBottom = (props) => {
 
 EditorNavbarBottom.propTypes = {
   navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
 };
 
-export default withUnstatedContainers(EditorNavbarBottom, [NavigationContainer]);
+export default withUnstatedContainers(EditorNavbarBottom, [NavigationContainer, EditorContainer, AppContainer]);

+ 1 - 24
src/client/js/components/SavePageControls.jsx

@@ -15,7 +15,6 @@ import AppContainer from '../services/AppContainer';
 import EditorContainer from '../services/EditorContainer';
 
 import { withUnstatedContainers } from './UnstatedUtils';
-import SlackNotification from './SlackNotification';
 import GrantSelector from './SavePageControls/GrantSelector';
 
 const logger = loggerFactory('growi:SavePageControls');
@@ -26,25 +25,14 @@ class SavePageControls extends React.Component {
     super(props);
 
     const config = this.props.appContainer.getConfig();
-    this.hasSlackConfig = config.hasSlackConfig;
     this.isAclEnabled = config.isAclEnabled;
 
-    this.slackEnabledFlagChangedHandler = this.slackEnabledFlagChangedHandler.bind(this);
-    this.slackChannelsChangedHandler = this.slackChannelsChangedHandler.bind(this);
     this.updateGrantHandler = this.updateGrantHandler.bind(this);
 
     this.save = this.save.bind(this);
     this.saveAndOverwriteScopesOfDescendants = this.saveAndOverwriteScopesOfDescendants.bind(this);
   }
 
-  slackEnabledFlagChangedHandler(isSlackEnabled) {
-    this.props.editorContainer.setState({ isSlackEnabled });
-  }
-
-  slackChannelsChangedHandler(slackChannels) {
-    this.props.editorContainer.setState({ slackChannels });
-  }
-
   updateGrantHandler(data) {
     this.props.editorContainer.setState(data);
   }
@@ -76,6 +64,7 @@ class SavePageControls extends React.Component {
   }
 
   render() {
+
     const { t, pageContainer, editorContainer } = this.props;
 
     const isRootPage = pageContainer.state.path === '/';
@@ -84,18 +73,6 @@ class SavePageControls extends React.Component {
 
     return (
       <div className="d-flex align-items-center form-inline">
-        {this.hasSlackConfig
-          && (
-          <div className="mr-2">
-            <SlackNotification
-              isSlackEnabled={editorContainer.state.isSlackEnabled}
-              slackChannels={editorContainer.state.slackChannels}
-              onEnabledFlagChange={this.slackEnabledFlagChangedHandler}
-              onChannelChange={this.slackChannelsChangedHandler}
-            />
-          </div>
-          )
-        }
 
         {this.isAclEnabled
           && (

+ 21 - 0
src/client/js/components/SlackLogo.jsx

@@ -0,0 +1,21 @@
+import React from 'react';
+
+const SlackLogo = () => (
+  <svg
+    xmlns="http://www.w3.org/2000/svg"
+    viewBox="0 0 448 448"
+    height="20"
+    width="20"
+  >
+    <path
+      d="M94.12,283.1A47.06,47.06,0,1,1,47.06,236H94.12Zm23.72,
+      0a47.06,47.06,0,1,1,94.12,0V400.94a47.06,47.06,0,1,1-94.12,0Zm47.06-189A47.06,
+      47.06,0,1,1,212,47.06V94.12Zm0,23.72a47.06,47.06,0,0,1,0,94.12H47.06a47.06,47.06,
+      0,0,1,0-94.12Zm189,47.06A47.06,47.06,0,1,1,400.94,212H353.88V164.9Zm-23.72,0a47.06,
+      47.06,0,1,1-94.12,0V47.06a47.06,47.06,0,1,1,94.12,0V164.9Zm-47.06,189A47.06,47.06,
+      0,1,1,236,400.94V353.88Zm0-23.72a47.06,47.06,0,0,1,0-94.12H400.94a47.06,47.06,0,0,1,0,94.12Z"
+    />
+  </svg>
+);
+
+export default SlackLogo;

+ 17 - 15
src/client/js/components/SlackNotification.jsx

@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
-
 /**
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
@@ -39,22 +38,23 @@ class SlackNotification extends React.Component {
     const { t } = this.props;
 
     return (
-      <div className="grw-slack-notification">
-        <div className="input-group input-group-sm extended-setting">
-          <label className="input-group-addon bg-light">
-            <img id="slack-mark-white" alt="slack-mark" src="/images/icons/slack/mark-monochrome_white.svg" width="18" height="18" />
-            <img id="slack-mark-black" alt="slack-mark" src="/images/icons/slack/mark-monochrome_black.svg" width="18" height="18" />
-
-            <input
-              type="checkbox"
-              value="1"
-              checked={this.props.isSlackEnabled}
-              onChange={this.updateCheckboxHandler}
-            />
-
+      <div className="grw-slack-notification w-100">
+        <div className="grw-input-group-slack-notification input-group extended-setting">
+          <label className="input-group-addon">
+            <div className="custom-control custom-switch custom-switch-lg custom-switch-slack">
+              <input
+                type="checkbox"
+                className="custom-control-input border-0"
+                id={this.props.id}
+                checked={this.props.isSlackEnabled}
+                onChange={this.updateCheckboxHandler}
+              />
+              <label className="custom-control-label align-center" htmlFor={this.props.id}>
+              </label>
+            </div>
           </label>
           <input
-            className="form-control"
+            className="grw-form-control-slack-notification form-control align-top pl-0"
             type="text"
             value={this.props.slackChannels}
             placeholder="Input channels"
@@ -75,10 +75,12 @@ class SlackNotification extends React.Component {
 SlackNotification.propTypes = {
   t: PropTypes.func.isRequired, // i18next
 
+  popUp: PropTypes.bool.isRequired,
   isSlackEnabled: PropTypes.bool.isRequired,
   slackChannels: PropTypes.string.isRequired,
   onEnabledFlagChange: PropTypes.func,
   onChannelChange: PropTypes.func,
+  id: PropTypes.string.isRequired,
 };
 
 export default withTranslation()(SlackNotification);

+ 0 - 20
src/client/styles/scss/_on-edit.scss

@@ -122,26 +122,6 @@ body.on-edit {
   .grw-editor-navbar-bottom {
     height: $grw-editor-navbar-bottom-height;
 
-    .grw-slack-notification {
-      .input-group-addon {
-        padding: 2px 8px;
-        line-height: 1em;
-        img,
-        input {
-          vertical-align: middle;
-        }
-      }
-      .form-control {
-        width: 80px;
-        @include media-breakpoint-up(sm) {
-          width: 130px;
-        }
-        @include media-breakpoint-up(md) {
-          width: 180px;
-        }
-      }
-    }
-
     .grw-grant-selector {
       @include media-breakpoint-down(sm) {
         .btn .label {

+ 1 - 0
src/client/styles/scss/_override-bootstrap-variables.scss

@@ -124,3 +124,4 @@ $pre-color: dummyinvalildcolor; // disable pre color specification with invalid
 $custom-checkbox-indicator-border-radius: 0px;
 $custom-control-indicator-focus-box-shadow: none;
 $custom-control-indicator-size: 1.2rem;
+

+ 54 - 0
src/client/styles/scss/atoms/_custom_control.scss

@@ -32,3 +32,57 @@ label.custom-control-label {
     }
   }
 }
+
+//lg
+.custom-switch.custom-switch-lg {
+  $custom-control-indicator-size-lg: $custom-control-indicator-size * 1.5;
+  $custom-switch-width-lg: $custom-control-indicator-size-lg * 1.75;
+  $custom-control-gutter-lg: $custom-control-gutter * 1.5;
+  $custom-control-indicator-size-lg: $custom-control-indicator-size * 1.5;
+  $custom-switch-indicator-size-lg: subtract($custom-control-indicator-size-lg, $custom-control-indicator-border-width * 4);
+
+  padding-left: $custom-switch-width-lg + $custom-control-gutter-lg;
+
+  line-height: $custom-control-indicator-size-lg;
+  .custom-control-label {
+    &::before {
+      top: ($font-size-base * $line-height-base - $custom-control-indicator-size-lg) / 2;
+
+      left: -($custom-switch-width-lg + $custom-control-gutter-lg);
+      width: $custom-switch-width-lg;
+      height: $custom-control-indicator-size-lg;
+      border-radius: $custom-control-indicator-size-lg/2;
+    }
+
+    &::after {
+      top: add(($font-size-base * $line-height-base - $custom-control-indicator-size-lg) / 2, $custom-control-indicator-border-width * 2);
+      left: add(-($custom-switch-width-lg + $custom-control-gutter-lg), $custom-control-indicator-border-width * 2);
+      width: $custom-switch-indicator-size-lg;
+      height: $custom-switch-indicator-size-lg;
+      border-radius: $custom-control-indicator-size-lg/2;
+    }
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::after {
+      transform: translateX($custom-switch-width-lg - $custom-control-indicator-size-lg);
+    }
+  }
+}
+
+.custom-switch.custom-switch-slack {
+  .custom-control-label {
+    &::before {
+      background-color: $gray-200;
+      border-color: transparent;
+    }
+    &::after {
+      background-size: 15px;
+    }
+  }
+  .input-group-addon {
+    input {
+      vertical-align: middle;
+    }
+  }
+}

+ 42 - 0
src/client/styles/scss/molecules/slack-notification.scss

@@ -0,0 +1,42 @@
+.grw-slack-notification {
+  $input-height-slack: $custom-control-indicator-size * 1.5;
+  border-color: $gray-200;
+
+  border-style: solid;
+  border-width: 1px;
+  border-radius: $input-height-slack/2 2px 2px $input-height-slack/2;
+
+  .form-control {
+    height: $input-height-slack;
+    border: transparent;
+    @include media-breakpoint-up(sm) {
+      width: 130px;
+    }
+    @include media-breakpoint-up(md) {
+      width: 180px;
+    }
+  }
+  // height settings for slack button's responsive design
+  // in the input and form-control element
+  .grw-form-control-slack-notification {
+    height: $input-height-slack;
+  }
+  .grw-input-group-slack-notification {
+    height: $input-height-slack;
+    label {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 0;
+    }
+  }
+
+  .custom-control-label {
+    &::before {
+      border: transparent;
+    }
+  }
+}
+// TODO デザインの使用が確定して実装、本タスクのスコープ外
+// .grw-slack-notification-xd {
+// }

+ 1 - 0
src/client/styles/scss/style-app.scss

@@ -23,6 +23,7 @@
 
 // molecules
 @import 'molecules/copy-dropdown';
+@import 'molecules/slack-notification';
 
 // growi component
 @import 'admin';

+ 42 - 0
src/client/styles/scss/theme/_apply-colors-dark.scss

@@ -281,6 +281,48 @@ body.on-edit {
   }
 }
 
+.grw-slack-notification {
+  background-color: transparent;
+  $color-slack: #4b144c;
+
+  .custom-control-label {
+    &::before {
+      background-color: $secondary;
+      border-color: transparent;
+    }
+    &::after {
+      background-color: darken($color-slack, 5%);
+      background-image: url(/images/icons/slack/slack-logo-dark-off.svg);
+    }
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::before {
+      background-color: lighten($color-slack, 10%);
+    }
+    &::after {
+      background-color: darken($color-slack, 5%);
+      background-image: url(/images/icons/slack/slack-logo-dark-on.svg);
+    }
+  }
+}
+
+.grw-slack-logo svg {
+  fill: #dd80de;
+}
+
+.grw-btn-slack {
+  background-color: black;
+  &:focus,
+  &:hover {
+    background-color: black;
+  }
+}
+
+.grw-btn-slack-triangle {
+  color: $secondary;
+}
+
 /*
  * GROWI HandsontableModal
  */

+ 45 - 0
src/client/styles/scss/theme/_apply-colors-light.scss

@@ -36,6 +36,10 @@ $table-hover-bg: $bgcolor-table-hover;
   background-color: $bgcolor-global;
 }
 
+.form-control::placeholder {
+  color: darken($bgcolor-global, 20%);
+}
+
 .form-control[disabled],
 .form-control[readonly] {
   color: lighten($color-global, 10%);
@@ -203,6 +207,47 @@ $table-hover-bg: $bgcolor-table-hover;
   }
 }
 
+.grw-slack-notification {
+  background-color: white;
+  $color-slack: #4b144c;
+
+  .custom-control-label {
+    &::before {
+      background-color: $gray-200;
+      border-color: transparent;
+    }
+    &::after {
+      background-color: white;
+      background-image: url(/images/icons/slack/slack-logo-off.svg);
+    }
+  }
+  .custom-control-input:checked ~ .custom-control-label {
+    &::before {
+      background-color: lighten($color-slack, 60%);
+    }
+    &::after {
+      background-image: url(/images/icons/slack/slack-logo-on.svg);
+    }
+  }
+}
+
+.grw-slack-logo svg {
+  fill: #af30b0;
+}
+
+.grw-btn-slack {
+  background-color: white;
+
+  &:hover,
+  &:focus {
+    background-color: white;
+  }
+}
+
+.grw-btn-slack-triangle {
+  color: $secondary;
+}
+
 /*
  * GROWI HandsontableModal
  */