ahooks#useBoolean TypeScript Examples

The following examples show how to use ahooks#useBoolean. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: github-login.tsx    From bext with MIT License 6 votes vote down vote up
LoginLink: FC = ({ children = '登录' }) => {
  const [visible, { setFalse, setTrue }] = useBoolean(false);

  return (
    <>
      <Link
        underline
        onClick={() => {
          setTrue();
          trackEvent(Events.ghLoginClick);
        }}
      >
        {children}
      </Link>
      {visible ? (
        <Dialog
          hidden={false}
          onDismiss={setFalse}
          dialogContentProps={{
            type: DialogType.normal,
            title: '账户权限说明',
            subText:
              '脚本发布、更新需要仓库读写权限,请在稍后的授权登录页面中允许此权限,本站代码完全开源可放心授权,请前往更多查看。',
          }}
          modalProps={{ layerProps: { hostId: LAYER_HOST_ID } }}
        >
          <DialogFooter>
            <PrimaryButton
              onClick={() => (location.href = LOGIN_URL)}
              text="确定登录"
            />
          </DialogFooter>
        </Dialog>
      ) : null}
    </>
  );
}
Example #2
Source File: index.tsx    From scorpio-h5-design with MIT License 6 votes vote down vote up
function App(props: IProps) {
  // TODO:组件初始值问题,暂时通过判断是否来自用户的首次输入
  const [isInitial, setIsInitial] = useBoolean(true);
  const { value, onChange } = props;
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    editor = new E(ref.current);
    editor.config.onchange = (content:string)=>{
      setIsInitial.setFalse();
      onChange(content);
    };
    editor.create();

    return () => {
      editor && editor.destroy();
    };
  }, []);

  useEffect(()=>{
    if(isInitial && value && editor){
      editor.txt.html(value);
    }
  }, [value, isInitial]);

  return (
    <div>
      <div ref={ref}></div>
    </div>
  );
}
Example #3
Source File: build-preview.tsx    From bext with MIT License 5 votes vote down vote up
BuildPreview: FC = () => {
  const build = useBuild();
  const [modalVisible, { setTrue: showModal, setFalse: hideModal }] =
    useBoolean(false);
  const [url, setUrl] = useState('');
  const [debugWindow, setDebugWindow] = useState<Window | null>(null);

  const pushScript = () => {
    debugWindow?.postMessage(
      {
        type: 'bext/script',
        payload: build,
      },
      '*',
    );
  };

  return (
    <div className="flex-1 flex flex-col pt-2 h-full">
      <div>
        <DefaultButton className="h-6 px-1 mr-2" onClick={showModal}>
          调试
        </DefaultButton>
        <PrimaryButton className="h-6 px-1 mr-2" onClick={pushScript}>
          推送脚本
        </PrimaryButton>
      </div>
      <Modal
        isOpen={modalVisible}
        onDismiss={hideModal}
        layerProps={{ hostId: LAYER_HOST_ID }}
      >
        <div className="w-[640px]">
          <div className="p-4">
            请先点击
            <Link
              href={`https://cdn.staticaly.com/gh/ikkz/bext@${BUILD_HASH}/public/lib/debug-client.user.js`}
            >
              此处
            </Link>
            安装油猴脚本,然后在下方文本框中输入需要调试的链接,点击打开。
            回到开发页面后点击“推送脚本”即可让正在编写的代码在目标窗口中刷新执行。
            若推送之后没有反应,请检查目标页面油猴脚本是否生效,并重新执行打开操作。
          </div>
          <div className="flex items-center px-4">
            <TextField
              className="flex-1 mr-2"
              value={url}
              onChange={(_, newUrl) => setUrl(newUrl || '')}
            />
            <PrimaryButton
              onClick={() => {
                hideModal();
                setDebugWindow(window.open(url));
              }}
            >
              打开
            </PrimaryButton>
          </div>
        </div>
      </Modal>
      <div className="text-sm pb-2">
        脚本预览(错误警告在请按 F12 打开浏览器开发者工具查看)
      </div>
      <Editor
        value={build}
        options={{ readOnly: true }}
        language="javascript"
        className="flex-1"
      />
    </div>
  );
}
Example #4
Source File: config-setting.tsx    From bext with MIT License 4 votes vote down vote up
ConfigSetting: FC = () => {
  const [formKey, { inc: resetForm }] = useCounter(0);
  const { draft, setDraft } = useDraft();
  const [modalVisible, { setTrue: showModal, setFalse: hideModal }] =
    useBoolean(false);

  useEffect(() => {
    if (!modalVisible) {
      resetForm();
    }
  }, [modalVisible]);

  return (
    <div className="p-4">
      <div className="flex items-center justify-between">
        <Toggle
          label="启用安装配置"
          inlineLabel
          checked={!!draft?.configSchema}
          className="mb-0"
          onChange={() =>
            setDraft({
              configSchema: draft?.configSchema ? undefined : DEFAULT_SCHEMA,
              defaultConfig: undefined,
            })
          }
        />
        {draft?.configSchema ? <Link onClick={showModal}>配置表单</Link> : null}
      </div>
      {draft?.configSchema ? (
        <>
          <Panel
            isOpen={modalVisible}
            onDismiss={hideModal}
            type={PanelType.smallFluid}
            headerText="配置表单"
            styles={{
              content: {
                padding: 0,
                flex: 1,
              },
              scrollableContent: {
                minHeight: '100%',
                display: 'flex',
                flexDirection: 'column',
              },
            }}
            isLightDismiss
          >
            {modalVisible ? <SchemaEditor /> : null}
          </Panel>
          <Editor
            value={`// 使用示例
import config from '@bext/config';
console.log(config);
/* config = ${JSON.stringify(draft.defaultConfig, null, 2)}
具体使用方式可查看
https://bext.ketra.fun/meta/648648
*/
`}
            height="200px"
            language="javascript"
            options={{
              readOnly: true,
              lineNumbers: 'off',
              minimap: { enabled: false },
              folding: false,
            }}
          />

          <Separator>配置默认值</Separator>
          {draft?.configSchema ? (
            <JsonSchemaForm
              key={String(formKey)}
              schema={draft.configSchema}
              omitExtraData
              liveOmit
              formData={draft.defaultConfig}
              onChange={({ formData }) => setDraft({ defaultConfig: formData })}
              liveValidate
            >
              <></>
            </JsonSchemaForm>
          ) : null}
        </>
      ) : null}
    </div>
  );
}
Example #5
Source File: dev-header.tsx    From bext with MIT License 4 votes vote down vote up
DevHeader: FC = () => {
  const history = useHistory();
  const { draft, saveDraft } = useDraft();
  const [detailVisible, { setTrue: showPanel, setFalse: hidePanel }] =
    useBoolean(false);
  const inDev = useInDev();
  useEffect(() => {
    if (inDev && !draft?.id) {
      showPanel();
    }
  }, [inDev, showPanel, draft]);

  const [publishVisible, { setTrue: showPublish, setFalse: hidePublish }] =
    useBoolean(false);
  const theme = useTheme();
  const { notify } = useNotifications();

  const onDebug = async () => {
    const { id, name, version, source, defaultConfig } = draft!;
    if (id && name && version) {
      try {
        const build = await excuteCompile({
          meta: {
            id,
            name,
            version,
            source: source || '',
            defaultConfig,
          },
        });
        window.ReactNativeWebView?.postMessage(
          JSON.stringify({
            type: 'debug',
            payload: build,
          }),
        );
      } catch (error) {
        notify({
          message: '编译失败,请查看控制台',
          status: 'error',
        });
      }
    } else {
      notify({
        message: '请填写完整 ID,名称,版本号',
        status: 'warning',
      });
    }
  };

  const user = useObservableState(user$);

  return (
    <>
      <header
        className="px-6 flex items-center justify-between h-12"
        style={{
          borderBottom: `1px solid ${theme.semanticColors.bodyDivider}`,
        }}
      >
        {isBextClient ? null : (
          <CommandBarButton
            text="返回"
            iconProps={{ iconName: 'ChevronLeft' }}
            className="h-8"
            onClick={() => {
              saveDraft();
              history.goBack();
            }}
          />
        )}
        <div>
          <DefaultButton onClick={showPanel}>编辑详情</DefaultButton>
          {isBextClient ? (
            <DefaultButton className="ml-3" onClick={onDebug}>
              调试
            </DefaultButton>
          ) : null}
          <PrimaryButton
            className="ml-3"
            onClick={() => {
              if (user.status === 'complete') {
                showPublish();
                return;
              }
              notify({
                status: 'warning',
                message: '请前往更多页面登录后发布',
              });
            }}
          >
            准备发布
          </PrimaryButton>
          <EditDetail visible={detailVisible} hide={hidePanel} />
        </div>
      </header>
      <Dialog
        hidden={!publishVisible}
        onDismiss={hidePublish}
        dialogContentProps={{
          type: DialogType.normal,
          title: '填写版本更新信息',
        }}
        modalProps={{
          isBlocking: false,
          layerProps: { hostId: LAYER_HOST_ID },
        }}
      >
        <PublishDialog hide={hidePublish} />
      </Dialog>
    </>
  );
}
Example #6
Source File: install-button.tsx    From bext with MIT License 4 votes vote down vote up
InstallButton: FC = () => {
  const { currentMeta, metaLoading } = useMetaDetail();
  const [configVisible, { setTrue: showConfig, setFalse: hideConfig }] =
    useBoolean(false);

  const installedChecker = useMemoizedFn((): InstallStatus => {
    const box = browserMethods.call('installed', currentMeta);
    if (box.code === 0) {
      return box.result ? 'installed' : 'notinstalled';
    }
    return 'unknown';
  });

  const [installStatus, setInstallStatus] = useState(installedChecker);

  useInterval(() => {
    setInstallStatus(installedChecker);
  }, 1000);

  const { notify } = useNotifications();

  const showUninstall =
    installStatus === 'installed' && browserMethods.support('uninstall');

  const onUninstall = () => {
    trackEvent(Events.metaUninstall, currentMeta?.id);
    console.log(
      browserMethods.call('uninstall', {
        ...currentMeta,
        author: `bext/${currentMeta?.id}`,
      }),
    );
  };

  const onInstall = (build: string) => {
    trackEvent(Events.metaInstallSuccess, currentMeta?.id);
    console.log(
      browserMethods.call('install', {
        ...currentMeta,
        author: `bext/${currentMeta?.id}`,
        build,
      }),
    );
  };

  const { run: install, loading } = useRequest(
    async () => {
      const { id, name, version, source, defaultConfig } = currentMeta!;
      onInstall(
        await excuteCompile({
          meta: { id, name, version, source, defaultConfig },
        }),
      );
    },
    {
      manual: true,
      onError: () =>
        notify({
          message: '编译失败,请点击“更多” -> “报告问题”',
          status: 'error',
        }),
    },
  );

  return showUninstall ? (
    <PrimaryButton
      className="ml-2"
      onClick={onUninstall}
      disabled={metaLoading}
    >
      卸载
    </PrimaryButton>
  ) : (
    <>
      <PrimaryButton
        className="ml-2"
        onClick={currentMeta?.configSchema ? showConfig : install}
        text={loading ? '处理中' : '安装此版本'}
        disabled={loading || metaLoading}
      />
      {configVisible ? (
        <ConfigInstall onInstall={onInstall} hide={hideConfig} />
      ) : null}
    </>
  );
}
Example #7
Source File: tool-sandbox.tsx    From bext with MIT License 4 votes vote down vote up
ToolSandbox: FC<{
  src: string;
}> = ({ src }) => {
  const theme = useTheme();
  const historyPush = useHistoryPush();

  const [loading, { setFalse: setLoaded }] = useBoolean(true);

  const ref = useRef<HTMLIFrameElement>(null);

  const [jsonSchema, setJsonSchema] = useState<{
    key: string;
    title?: string;
    schema: any;
  } | null>(null);
  const [formData, setFormData] = useState(null);
  const [hasError, setHasError] = useState(false);

  const resetForm = () => {
    setJsonSchema(null);
    setFormData(null);
    setHasError(false);
  };

  const { setDraftObject } = useDraft();

  useEventListener('message', (event: MessageEvent) => {
    try {
      const url = new URL(src);
      if (![url.origin, location.origin].includes(event.origin)) {
        return;
      }
    } catch (e) {}
    const { data } = event;
    switch (data.type) {
      case 'bext/jsf-show':
        if (data.schema) {
          setJsonSchema({
            key: data.key,
            schema: data.schema,
          });
          setFormData(data.default || null);
        }
        break;
      case 'bext/meta-install':
        if (data.meta) {
          browserMethods.call('install', data.meta);
        }
        break;
      case 'bext/meta-draft':
        if (data.meta) {
          setDraftObject(data.meta);
          historyPush(DEV_PATH);
        }
        break;
      case 'bext/unknown-install':
        copy(data.payload.build);
        notify({
          message: '当前浏览器不支持安装,已将脚本内容复制到剪切板',
          status: 'info',
        });
        break;
      case 'bext/notify':
        if (data.payload) {
          notify(data.payload);
        }
        break;
      default:
        break;
    }
  });

  const { notify } = useNotifications();

  return (
    <div className="flex-1 relative">
      <iframe
        className="w-full h-full border-none"
        onLoad={setLoaded}
        src={src}
        ref={ref}
      />
      {loading ? (
        <Spinner
          className="absolute inset-0"
          style={{
            backgroundColor: theme.semanticColors.bodyBackground,
          }}
          size={SpinnerSize.large}
        />
      ) : null}
      {jsonSchema ? (
        <Dialog
          hidden={false}
          dialogContentProps={{
            type: DialogType.normal,
            title: jsonSchema.title || '选项',
          }}
          minWidth={400}
          modalProps={{ layerProps: { hostId: LAYER_HOST_ID } }}
        >
          <JsonSchemaForm
            schema={jsonSchema.schema}
            formData={formData}
            onChange={({ formData, errors }) => {
              setFormData(formData);
              setHasError(!!errors.length);
            }}
            omitExtraData
            liveOmit
            liveValidate
          >
            <></>
          </JsonSchemaForm>
          <DialogFooter>
            <PrimaryButton
              onClick={() => {
                ref.current?.contentWindow?.postMessage(
                  {
                    type: 'bext/jsf-submit',
                    value: formData,
                    key: jsonSchema.key,
                  },
                  '*',
                );
                resetForm();
              }}
              text="确定"
              disabled={hasError}
            />
            <DefaultButton onClick={resetForm} text="取消" />
          </DialogFooter>
        </Dialog>
      ) : null}
    </div>
  );
}
Example #8
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
export default function() {
  const { pageId, pageSchema, selectPage, setStateByObjectKeys } = useModel('bridge');
  const addPageReq = useRequest(service.addPage, {
    manual: true,
  });
  const editPageReq = useRequest(service.editPage, {
    manual: true,
  });
  const [visible, { toggle }] = useBoolean(false);
  const [qrcodeUrl, setQrcodeUrl] = useState('');

  const save = async function() {
    const dataURL = await window.postmate_mobile.get(childrenModel.CAPTURE);
    if (dataURL) {
      const file = dataURLtoFile(dataURL, new Date().getTime().toString());
      const fileName = `${uuidv4()}.png`;
      await ossClient.put(`design/${fileName}`, file);
      selectPage.cover = `https://scorpio-design.lxzyl.cn/design/${fileName}`;
    }
    let res;
    if (pageId) {
      res = await editPageReq.run({
        _id: pageId,
        pageSchema,
      });
    } else {
      res = await addPageReq.run({
        pageSchema,
      });
    }
    return res;
  };

  const onSave = async function() {
    if (pageSchema.length === 0) {
      return message.error('请新建页面后再保存!');
    }
    if (selectPage.components.length === 0) {
      return message.error('至少添加一个组件后再保存!');
    }
    const res = await save();
    const state = {
      pageId: res._id,
    };
    setStateByObjectKeys(state);
    message.success('保存成功!');
  };

  const onVisibleChange = async function() {
    if (pageSchema.length === 0) {
      return message.error('请新建页面后再操作!');
    }
    toggle();
    await save();
    const dataUrl = await QRCode.toDataURL(`${config.h5Base}?id=${pageId}`, {
      margin: 0,
    });
    console.log('dataUrl: ', dataUrl);
    setQrcodeUrl(dataUrl);
  };

  const overviewContent = (
    <div className="overview-qrcode">
      <img className="overview-qrcode-img" src={qrcodeUrl} />
    </div>
  );

  const exportJson = function() {
    const blob = new Blob([JSON.stringify(pageSchema)], { type: 'application/json' });
    console.log('pageSchema: ', pageSchema);
    FileSaver.saveAs(blob, `${pageSchema[0].props.title}.json`);
  };

  return (
    <div className="design-header">
      <div className="design-header-operations">
        <div className="item" onClick={() => { history.push('/manage/page'); }}>
          <i className="iconfont icon-shouye" />
          <div className="text" >首页</div>
        </div>
        <div className="item" onClick={onSave}>
          <i className="iconfont icon-baocun" />
          <div className="text" >保存</div>
          <Spin spinning={addPageReq.loading || editPageReq.loading}>
          </Spin>
        </div>
        {/* <Popover
          title="真机预览"
          trigger="click"
          visible={visible}
          onVisibleChange={onVisibleChange}
          content={overviewContent}
          overlayClassName="overview-qrcode-popover"
        >

          <div className="item">
            <i className="iconfont icon-shouji" />
            <div className="text">预览</div>
          </div>
        </Popover> */}
        <div className="item" onClick={exportJson}>
          <i className="iconfont icon-json" />
          <div className="text">导出</div>
        </div>
        {/* <div className="item">
          <i className="iconfont icon-html" />
          <div className="text">下载</div>
          <i className="iconfont icon-new" style={{position: 'absolute', color: 'red', right: 0, top: 0}}/>
        </div> */}
        <div className="item" onClick={() => { window.open('https://github.com/lx544690189/scorpio-h5-design'); }}>
          <i className="iconfont icon-github-fill" />
          <div className="text">GITHUB</div>
        </div>
      </div>
    </div>
  );
}
Example #9
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
export default function() {
  // @ts-expect-error
  const { _id } = history.location.query;
  const { setStateByObjectKeys } = useModel('bridge');
  const queryPageDetailReq = useRequest(service.queryPageDetail, {
    manual: true,
  });
  const [loading, setLoading] = useBoolean(true);

  useEffect(() => {
    initData();
    return ()=>{
      window.postmate_mobile.destroy();
    };
  }, []);

  /**
   * 初始化数据、编辑页面初始数据
   */
  const initData = async function() {
    setLoading.setTrue();
    // 加载iframe、发送请求、更新state会导致页面短时间卡顿,延时进行这些任务
    await sleep(100);
    let state = {
      pageId: undefined,
      pageSchema: [],
      selectPageIndex: -1,
      selectComponentId: undefined,
    };
    if (_id) {
      const res = await queryPageDetailReq.run({
        _id,
      });
      state = {
        pageId: res._id,
        pageSchema: res.pageSchema,
        selectPageIndex: 0,
        selectComponentId: undefined,
      };
    }
    setStateByObjectKeys(state, false);
    await sleep(100);
    const handshake = new Postmate({
      container: document.getElementById('mobile-content'),
      url: `${config.base}/#/mobile`,
      name: 'mobile',
      classListArray: ['mobile'],
    });
    handshake.then((child) => {
      window.postmate_mobile = child;
      syncState({
        payload: state,
        type: IMessageType.syncState,
      });
      // 注册事件
      child.on(childrenModel.SYNC_STATE, (message) => {
        setStateByObjectKeys(message, false);
      });
      setLoading.setFalse();
    });
  };

  return (
    <Spin
      spinning={useDebounce(loading, { wait: 500 })}
      wrapperClassName="blur-loading"
      indicator={<Loading />}
    >
      <div className="design">
        <Header />
        <div className="side-left">
          {!loading && <SideLeft />}
        </div>
        <div className="side-right">
          {!loading && <SideRight />}
        </div>
        <div className="center">
          <MobileSimulator loading={loading}/>
        </div>
      </div>
    </Spin>
  );
}
Example #10
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
export default function() {
  const [isOrdering, setIsOrdering] = useBoolean(false);
  const {
    isDraging, pageSchema, selectPageIndex, dragingComponentIndex, selectComponent,
    onDragEnter, onDragLeave, onDrop, onSelectComponent, onSortEnd, showSelectComponentBorder,
  } = useModel('bridge');
  let components:any[] = [];
  if (pageSchema.length > 0 && selectPageIndex !== -1) {
    components = pageSchema[selectPageIndex].components;
  }

  return (
    <DragDropContext
      onDragStart={()=>{
        setIsOrdering.setTrue();
      }}
      onDragEnd={(result: DropResult)=>{
        onSortEnd(result, components);
        setIsOrdering.setFalse();
      }}
    >
      <Droppable droppableId="droppable">
        {(provided, snapshot) => (
          <div
            {...provided.droppableProps}
            ref={provided.innerRef}
          >
            {components.length > 0 && components.map((item: any, index: any) => (
              <Draggable key={item.uuid} draggableId={item.uuid} index={index}>
                {(provided, snapshot) => (
                  <>
                    <div
                      className={classnames(
                        'h5-canvas-empty-block',
                        {
                          show: isDraging,
                          over: dragingComponentIndex === index,
                        }
                      )}
                      onDragEnter={() => { onDragEnter(index); }}
                      onDragLeave={() => { onDragLeave(); }}
                      onDragOver={(event) => { event.preventDefault(); }}
                      onDrop={(event)=>{onDrop(event, index);}}
                    />
                    <div
                      className={classnames(
                        'h5-canvas-block',
                        {
                          'blur': !snapshot.isDragging && isOrdering,
                          'isSelected': selectComponent?.uuid === item.uuid,
                          'border': selectComponent?.uuid === item.uuid && showSelectComponentBorder,
                        }
                      )}
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      onClick={() => { onSelectComponent(item.uuid); }}
                    >
                      <DynamicComponent
                        id={item._id}
                        uuid={item.uuid}
                        isSelected={selectComponent?.uuid === item.uuid}
                        componentProps={item.props}
                        containerProps={item.containerProps}
                      />
                    </div>
                  </>
                )}
              </Draggable>
            ))}
            {provided.placeholder}
            <div
              className={classnames(
                'h5-canvas-empty-block',
                {
                  show: isDraging,
                  over: dragingComponentIndex === components.length,
                }
              )}
              onDragEnter={() => { onDragEnter(components.length); }}
              onDragLeave={() => { onDragLeave(); }}
              onDragOver={(event) => { event.preventDefault(); }}
              onDrop={(event)=>{onDrop(event, components.length);}}
            />
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}