mirror of https://github.com/msgbyte/tailchat
				
				
				
			feat: 增加intro插件用于新人引导
							parent
							
								
									e697527caf
								
							
						
					
					
						commit
						fcaa941e9a
					
				@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "label": "初始引导插件",
 | 
			
		||||
  "name": "com.msgbyte.intro",
 | 
			
		||||
  "url": "/plugins/com.msgbyte.intro/index.js",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "author": "msgbyte",
 | 
			
		||||
  "description": "为应用首次打开介绍应用的能力",
 | 
			
		||||
  "requireRestart": true
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@plugins/com.msgbyte.intro",
 | 
			
		||||
  "main": "src/index.ts",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "shepherd.js": "^8.3.1"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,4 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 异步加载
 | 
			
		||||
 */
 | 
			
		||||
import('./tour');
 | 
			
		||||
@ -0,0 +1,77 @@
 | 
			
		||||
import Shepherd from 'shepherd.js';
 | 
			
		||||
 | 
			
		||||
function buildWatchDom(selector: string) {
 | 
			
		||||
  return () => {
 | 
			
		||||
    return new Promise<void>((resolve) => {
 | 
			
		||||
      const findDom = () => {
 | 
			
		||||
        if (document.querySelector(selector)) {
 | 
			
		||||
          resolve();
 | 
			
		||||
        } else {
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
            findDom();
 | 
			
		||||
          }, 200);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      findDom();
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function buildStepOption(options: {
 | 
			
		||||
  id: string;
 | 
			
		||||
  text: string;
 | 
			
		||||
  selector: string;
 | 
			
		||||
  position?: Shepherd.Step.StepOptions['attachTo']['on'];
 | 
			
		||||
  canClickTarget?: boolean;
 | 
			
		||||
}): Shepherd.Step.StepOptions {
 | 
			
		||||
  return {
 | 
			
		||||
    id: options.id,
 | 
			
		||||
    text: options.text,
 | 
			
		||||
    attachTo: {
 | 
			
		||||
      element: options.selector,
 | 
			
		||||
      on: options.position ?? 'auto',
 | 
			
		||||
    },
 | 
			
		||||
    canClickTarget: false,
 | 
			
		||||
    beforeShowPromise: buildWatchDom(options.selector),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const steps: Shepherd.Step.StepOptions[] = [
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'navbar',
 | 
			
		||||
    text: '这是导航栏, 在这里可以切换tailchat的各个主要页面',
 | 
			
		||||
    selector: '[data-tc-role=navbar]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'personal',
 | 
			
		||||
    text: '这是个人信息,在这里可以管理您的好友、插件、以及私信',
 | 
			
		||||
    selector: '[data-tc-role=navbar-personal]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'groups',
 | 
			
		||||
    text: '这是群组列表, 会显示所有已加入的群组,您可以通过点击切换切换群组,也可以点击 + 号按钮来创建群组',
 | 
			
		||||
    selector: '[data-tc-role=navbar-groups]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'settings',
 | 
			
		||||
    text: '这是设置按钮,可以通过此按钮来进行个人信息的变更、系统设置的变更、软件信息等内容',
 | 
			
		||||
    selector: '[data-tc-role=navbar-settings]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'sidebar',
 | 
			
		||||
    text: '这是侧边栏,用于切换内容',
 | 
			
		||||
    selector: '[data-tc-role^=sidebar-]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
  buildStepOption({
 | 
			
		||||
    id: 'content',
 | 
			
		||||
    text: '这是内容区,用于显示主要内容',
 | 
			
		||||
    selector: '[data-tc-role^=content-]',
 | 
			
		||||
    position: 'right',
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
@ -0,0 +1,176 @@
 | 
			
		||||
.shepherd-button {
 | 
			
		||||
  background: #3288e6;
 | 
			
		||||
  border: 0;
 | 
			
		||||
  border-radius: 3px;
 | 
			
		||||
  color: hsla(0, 0%, 100%, 0.75);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  margin-right: 0.5rem;
 | 
			
		||||
  padding: 0.5rem 1.5rem;
 | 
			
		||||
  transition: all 0.5s ease;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-button:not(:disabled):hover {
 | 
			
		||||
  background: #196fcc;
 | 
			
		||||
  color: hsla(0, 0%, 100%, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-button.shepherd-button-secondary {
 | 
			
		||||
  background: #f1f2f3;
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-button.shepherd-button-secondary:not(:disabled):hover {
 | 
			
		||||
  background: #d6d9db;
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-button:disabled {
 | 
			
		||||
  cursor: not-allowed;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-footer {
 | 
			
		||||
  border-bottom-left-radius: 5px;
 | 
			
		||||
  border-bottom-right-radius: 5px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  padding: 0 0.75rem 0.75rem;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-footer .shepherd-button:last-child {
 | 
			
		||||
  margin-right: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-cancel-icon {
 | 
			
		||||
  background: transparent;
 | 
			
		||||
  border: none;
 | 
			
		||||
  color: hsla(0, 0%, 50.2%, 0.75);
 | 
			
		||||
  font-size: 2em;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  font-weight: 400;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  transition: color 0.5s ease;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-cancel-icon:hover {
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-has-title .shepherd-content .shepherd-cancel-icon {
 | 
			
		||||
  color: hsla(0, 0%, 50.2%, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-has-title .shepherd-content .shepherd-cancel-icon:hover {
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
}
 | 
			
		||||
.shepherd-title {
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
  display: flex;
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  font-weight: 400;
 | 
			
		||||
  flex: 1 0 auto;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-header {
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  border-top-left-radius: 5px;
 | 
			
		||||
  border-top-right-radius: 5px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  line-height: 2em;
 | 
			
		||||
  padding: 0.75rem 0.75rem 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-has-title .shepherd-content .shepherd-header {
 | 
			
		||||
  background: #e6e6e6;
 | 
			
		||||
  padding: 1em;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-text {
 | 
			
		||||
  color: rgba(0, 0, 0, 0.75);
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  line-height: 1.3em;
 | 
			
		||||
  padding: 0.75em;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-text p {
 | 
			
		||||
  margin-top: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-text p:last-child {
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-content {
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element {
 | 
			
		||||
  background: #fff;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
 | 
			
		||||
  max-width: 400px;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  transition: opacity 0.3s, visibility 0.3s;
 | 
			
		||||
  visibility: hidden;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  z-index: 9999;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-enabled.shepherd-element {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  visibility: visible;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  visibility: hidden;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element,
 | 
			
		||||
.shepherd-element *,
 | 
			
		||||
.shepherd-element :after,
 | 
			
		||||
.shepherd-element :before {
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-arrow,
 | 
			
		||||
.shepherd-arrow:before {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  width: 16px;
 | 
			
		||||
  height: 16px;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-arrow:before {
 | 
			
		||||
  content: '';
 | 
			
		||||
  transform: rotate(45deg);
 | 
			
		||||
  background: #fff;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element[data-popper-placement^='top'] > .shepherd-arrow {
 | 
			
		||||
  bottom: -8px;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element[data-popper-placement^='bottom'] > .shepherd-arrow {
 | 
			
		||||
  top: -8px;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element[data-popper-placement^='left'] > .shepherd-arrow {
 | 
			
		||||
  right: -8px;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element[data-popper-placement^='right'] > .shepherd-arrow {
 | 
			
		||||
  left: -8px;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element.shepherd-centered > .shepherd-arrow {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-element.shepherd-has-title[data-popper-placement^='bottom']
 | 
			
		||||
  > .shepherd-arrow:before {
 | 
			
		||||
  background-color: #e6e6e6;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,
 | 
			
		||||
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target * {
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-modal-overlay-container {
 | 
			
		||||
  height: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  transition: all 0.3s ease-out, height 0ms 0.3s, opacity 0.3s 0ms;
 | 
			
		||||
  width: 100vw;
 | 
			
		||||
  z-index: 9997;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-modal-overlay-container.shepherd-modal-is-visible {
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  opacity: 0.5;
 | 
			
		||||
  transition: all 0.3s ease-out, height 0s 0s, opacity 0.3s 0s;
 | 
			
		||||
}
 | 
			
		||||
.shepherd-modal-overlay-container.shepherd-modal-is-visible path {
 | 
			
		||||
  pointer-events: all;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,41 @@
 | 
			
		||||
import Shepherd from 'shepherd.js';
 | 
			
		||||
import { steps } from './steps';
 | 
			
		||||
import './style.less';
 | 
			
		||||
 | 
			
		||||
const KEY = 'com.msgbyte.intro/hasRun';
 | 
			
		||||
 | 
			
		||||
if (!window.localStorage.getItem(KEY)) {
 | 
			
		||||
  const tour = new Shepherd.Tour({
 | 
			
		||||
    useModalOverlay: true,
 | 
			
		||||
    defaultStepOptions: {
 | 
			
		||||
      classes: 'shadow-md',
 | 
			
		||||
      scrollTo: true,
 | 
			
		||||
      arrow: false,
 | 
			
		||||
      modalOverlayOpeningRadius: 4,
 | 
			
		||||
      modalOverlayOpeningPadding: 4,
 | 
			
		||||
      buttons: [
 | 
			
		||||
        {
 | 
			
		||||
          text: '跳过引导',
 | 
			
		||||
          secondary: true,
 | 
			
		||||
          action() {
 | 
			
		||||
            this.complete();
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: '下一步',
 | 
			
		||||
          action() {
 | 
			
		||||
            this.next();
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  tour.on('complete', () => {
 | 
			
		||||
    window.localStorage.setItem(KEY, 'true');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  tour.addSteps(steps);
 | 
			
		||||
 | 
			
		||||
  tour.start();
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "rootDir": "./src",
 | 
			
		||||
    "baseUrl": "./src",
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "jsx": "react",
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@capital/*": ["../../../src/plugin/*"],
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
 | 
			
		||||
# yarn lockfile v1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
"@popperjs/core@^2.9.2":
 | 
			
		||||
  version "2.10.1"
 | 
			
		||||
  resolved "https://registry.nlark.com/@popperjs/core/download/@popperjs/core-2.10.1.tgz#728ecd95ab207aab8a9a4e421f0422db329232be"
 | 
			
		||||
  integrity sha1-co7NlasgequKmk5CHwQi2zKSMr4=
 | 
			
		||||
 | 
			
		||||
deepmerge@^4.2.2:
 | 
			
		||||
  version "4.2.2"
 | 
			
		||||
  resolved "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
 | 
			
		||||
  integrity sha1-RNLqNnm49NT/ujPwPYZfwee/SVU=
 | 
			
		||||
 | 
			
		||||
shepherd.js@^8.3.1:
 | 
			
		||||
  version "8.3.1"
 | 
			
		||||
  resolved "https://registry.nlark.com/shepherd.js/download/shepherd.js-8.3.1.tgz#131eeefc5eb2bc44c9e23d0da139db46b2b55339"
 | 
			
		||||
  integrity sha1-Ex7u/F6yvETJ4j0NoTnbRrK1Uzk=
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@popperjs/core" "^2.9.2"
 | 
			
		||||
    deepmerge "^4.2.2"
 | 
			
		||||
    smoothscroll-polyfill "^0.4.4"
 | 
			
		||||
 | 
			
		||||
smoothscroll-polyfill@^0.4.4:
 | 
			
		||||
  version "0.4.4"
 | 
			
		||||
  resolved "https://registry.nlark.com/smoothscroll-polyfill/download/smoothscroll-polyfill-0.4.4.tgz#3a259131dc6930e6ca80003e1cb03b603b69abf8"
 | 
			
		||||
  integrity sha1-OiWRMdxpMObKgAA+HLA7YDtpq/g=
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue