office-ui-fabric-react#MessageBarType TypeScript Examples

The following examples show how to use office-ui-fabric-react#MessageBarType. 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: MessageInfo.tsx    From AIPerf with MIT License 6 votes vote down vote up
render(): React.ReactNode {
        const { info, typeInfo, className } = this.props;
        return (
            <MessageBar
                messageBarType={MessageBarType[typeInfo]}
                isMultiline={false}
                className={className}
            >
                {info}
            </MessageBar>
        );
    }
Example #2
Source File: ErrorMessageBar.tsx    From hypertrons-crx with Apache License 2.0 6 votes vote down vote up
ErrorMessageBar: React.FC<ErrorMessageBarProps> = ({
  errorCode = ErrorCode.UNKNOWN,
  url = HYPERTRONS_CRX_NEW_ISSUE,
}) => {
  const [inited, setInited] = useState(false);
  const [settings, setSettings] = useState(new Settings());

  useEffect(() => {
    const initSettings = async () => {
      const temp = await loadSettings();
      setSettings(temp);
      setInited(true);
    };
    if (!inited) {
      initSettings();
    }
  }, [inited, settings]);

  return (
    <Stack>
      <Stack.Item align="center">
        <MessageBar
          messageBarType={MessageBarType.error}
          isMultiline={false}
          dismissButtonAriaLabel="Close"
        >
          {getMessageByLocale('global_error_message', settings.locale)}
          {errorCode}.
          <Link href={url} target="_blank" underline>
            {getMessageByLocale('global_clickToshow', settings.locale)} Issue.
          </Link>
        </MessageBar>
      </Stack.Item>
    </Stack>
  );
}
Example #3
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
ErrorMessage = () => (
  <MessageBar
    messageBarType={MessageBarType.error}
    isMultiline={false}
    dismissButtonAriaLabel="Close"
  >
    Error MessageBar with single line, with dismiss button.
    <Link href="www.bing.com" target="_blank">
      Visit our website.
    </Link>
  </MessageBar>
)
Example #4
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
AccessMessage = () => (
  <MessageBar
    messageBarType={MessageBarType.blocked}
    isMultiline={false}
    dismissButtonAriaLabel="Close"
    truncated={true}
    overflowButtonAriaLabel="See more"
  >
    <b>Blocked MessageBar - single line, with dismiss button and truncated text.</b> Truncation is not available if you
    use action buttons or multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing
    elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum
    aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac
    efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet
    faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce
    massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget,
    condimentum mauris.
  </MessageBar>
)
Example #5
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
WarningMessage = () => (
  <MessageBar
    messageBarType={MessageBarType.severeWarning}
    actions={
      <div>
        <MessageBarButton onClick={() => console.log('Yes clicked')}>Yes</MessageBarButton>
        <MessageBarButton onClick={() => console.log('No clicked')}>No</MessageBarButton>
      </div>
    }
  >
    SevereWarning MessageBar with action buttons which defaults to multiline.
    <Link href="www.bing.com" target="_blank">
      Visit our website.
    </Link>
  </MessageBar>
)
Example #6
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
SuccessQuestion = () => (
  <MessageBar
    actions={
      <div>
        <MessageBarButton onClick={() => console.log('Yes clicked')}>Yes</MessageBarButton>
        <MessageBarButton onClick={() => console.log('No clicked')}>No</MessageBarButton>
      </div>
    }
    messageBarType={MessageBarType.success}
    isMultiline={false}
  >
    Success MessageBar with single line and action buttons.
    <Link href="www.bing.com" target="_blank">
      Visit our website.
    </Link>
  </MessageBar>
)
Example #7
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
WarningQuestion = () => (
  <MessageBar
    messageBarType={MessageBarType.warning}
    isMultiline={false}
    dismissButtonAriaLabel="Close"
    actions={
      <div>
        <MessageBarButton onClick={() => console.log('Action clicked')}>Action</MessageBarButton>
      </div>
    }
  >
    Warning MessageBar content.
    <Link href="www.bing.com" target="_blank">
      Visit our website.
    </Link>
  </MessageBar>
)
Example #8
Source File: SpfxFluentuiMessagebar.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
WarningLongMessage = () => (
  <MessageBar
    dismissButtonAriaLabel="Close"
    messageBarType={MessageBarType.warning}
    actions={
      <div>
        <MessageBarButton>Yes</MessageBarButton>
        <MessageBarButton>No</MessageBarButton>
      </div>
    }
  >
    <b>Warning defaults to multiline</b>. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a
    lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper
    scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras
    faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse
    platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu
    mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris.
    <Link href="www.bing.com" target="_blank">
      Visit our website.
    </Link>
  </MessageBar>
)
Example #9
Source File: NetworkViewPCF.tsx    From NetworkViewPCF with MIT License 5 votes vote down vote up
render() {
    const { vm, width, height } = this.props;
    const { fullScreen } = vm;
    let correctedWidth = width;
    if (!fullScreen) correctedWidth = width - 22;
    const { currentLink, currentNode, calloutTarget, serviceProvider } = this.props.vm;
    console.debug("NetworkView:render");
    // https://github.com/vasturiano/react-force-graph
    return (
      <>
        <Stack
          style={{
            boxShadow: "inset 0 0 10px #000000",
            background: "radial-gradient(circle, rgba(255,255,255,1) 0%,  rgba(220,228,236,1) 100%)",
            width: correctedWidth,
          }}
        >
          <Stack verticalFill={false} style={{ position: "absolute", zIndex: 99999 }}>
            {!vm.errorText && (
              <StackItem shrink>
                <IconButton onClick={this.onFullScreen} iconProps={{ iconName: "FullScreen" }}></IconButton>
                <IconButton onClick={this.zoomToFit} iconProps={{ iconName: "Zoom" }}></IconButton>
              </StackItem>
            )}
            <StackItem styles={{ root: { paddingLeft: 20 } }}>
              <LoadProgress vm={vm} />
            </StackItem>
            {vm.errorText && (
              <StackItem shrink styles={{ root: { width: width - 80, padding: 10, margin: 20 } }}>
                <MessageBar messageBarType={MessageBarType.blocked} isMultiline={true}>
                  {vm.errorText}
                </MessageBar>
              </StackItem>
            )}
          </Stack>

          <ForceGraph2D
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            ref={this.gref as any}
            width={width}
            height={height}
            graphData={vm.data}
            nodeLabel={this.nodeLabel}
            nodeCanvasObject={this.renderNode}
            linkCanvasObject={this.linkCanvasObject}
            enableZoomPanInteraction={true}
            onNodeHover={this.onHoverNode}
            onNodeClick={this.onNodeClick}
            onLinkHover={this.onLinkHover}
            onLinkClick={this.onLinkClick}
            onZoomEnd={this.onZoomEnd}
          />
        </Stack>

        {(currentNode || currentLink) && (
          <Callout
            target={calloutTarget}
            onDismiss={this.onCalloutDismiss}
            directionalHint={DirectionalHint.bottomCenter}
            setInitialFocus={true}
          >
            <RecordDetails node={currentNode} link={currentLink} serviceProvider={serviceProvider} />
            {currentLink &&
              currentLink.otherLinks &&
              currentLink.otherLinks.map((link, index) => (
                <>
                  <RecordDetails link={link} serviceProvider={serviceProvider} />
                </>
              ))}
          </Callout>
        )}
      </>
    );
  }
Example #10
Source File: PeopleSearchContainer.tsx    From spfx-msgraph-peoplesearch with MIT License 4 votes vote down vote up
/**
   *
   *
   * @returns {React.ReactElement<IPeopleSearchContainerProps>}
   * @memberof Directory
   */
  public render(): React.ReactElement<IPeopleSearchContainerProps> {

    const areResultsLoading = this.state.areResultsLoading;
    const items = this.state.results[this.state.page - 1];
    const hasError = this.state.hasError;
    const errorMessage = this.state.errorMessage;

    const { semanticColors }: IReadonlyTheme = this.props.themeVariant;

    let renderWebPartTitle: JSX.Element = null;
    let renderWebPartContent: JSX.Element = null;
    let renderOverlay: JSX.Element = null;
    let renderShimmerElements: JSX.Element = null;
    let renderSearchBox: JSX.Element = null;
    let renderPagination: JSX.Element = null;

    // Loading behavior
    if (areResultsLoading) {
      if (!isEmpty(items.value)) {
        renderOverlay = <div>
            <Overlay isDarkThemed={false} theme={this.props.themeVariant as ITheme} className={styles.overlay}>
                <Spinner size={SpinnerSize.medium} />
            </Overlay>
        </div>;
      } else {
        let templateContext = {
          items: items,
          resultCount: this.state.resultCount,
          showPagination: this.props.showPagination,
          showResultsCount: this.props.showResultsCount,
          showBlank: this.props.showBlank && this.props.searchParameterOption !== SearchParameterOption.SearchBox,
          showLPC: this.props.showLPC,
          themeVariant: this.props.themeVariant,
          pageSize: this.props.searchService.pageSize,
          serviceScope: this.props.serviceScope
        } as ITemplateContext;
        templateContext = { ...templateContext, ...this.props.templateParameters };
  
        renderShimmerElements = this.props.templateService.getShimmerTemplateComponent(this.props.selectedLayout, templateContext);
      }
    }

    // WebPart title
    renderWebPartTitle = <WebPartTitle displayMode={this.props.displayMode} title={this.props.webPartTitle} updateProperty={(value: string) => this.props.updateWebPartTitle(value)} />;

    // WebPart content
    if (isEmpty(items.value) && this.props.showBlank && this.props.selectedLayout !== ResultsLayoutOption.Debug && this.props.searchParameterOption !== SearchParameterOption.SearchBox) {
      if (this.props.displayMode === DisplayMode.Edit) {
        renderWebPartContent = <MessageBar messageBarType={MessageBarType.info}>{strings.ShowBlankEditInfoMessage}</MessageBar>;
      }
      else {
        renderWebPartTitle = null;
      }
    } else {
      let templateContext = {
        items: items,
        resultCount: this.state.resultCount,
        showPagination: this.props.showPagination,
        showResultsCount: this.props.showResultsCount,
        showBlank: this.props.showBlank && this.props.searchParameterOption !== SearchParameterOption.SearchBox,
        showLPC: this.props.showLPC,
        themeVariant: this.props.themeVariant,
        pageSize: this.props.searchService.pageSize,
        serviceScope: this.props.serviceScope
      } as ITemplateContext;
      templateContext = { ...templateContext, ...this.props.templateParameters };

      let renderSearchResultTemplate = this.props.templateService.getTemplateComponent(this.props.selectedLayout, templateContext);

      if (this.props.searchParameterOption === SearchParameterOption.SearchBox) {
        renderSearchBox = <PeopleSearchBox themeVariant={this.props.themeVariant} onSearch={(searchQuery) => { this.props.updateSearchParameter(searchQuery); }} searchInputValue={this.props.searchService.searchParameter} />;
      }

      if (this.props.showPagination) {
        let prevPageEl: JSX.Element = null;
        let nextPageEl: JSX.Element = null;

        if (this.hasPreviousPage()) {
          prevPageEl = <IconButton onClick={async () => await this._fetchPeopleSearchResults(this.state.page - 1)} iconProps={{ iconName: 'DoubleChevronLeft8' }} />;
        }

        if (this.hasNextPage()) {
          nextPageEl = <IconButton onClick={async () => await this._fetchPeopleSearchResults(this.state.page + 1)} iconProps={{ iconName: 'DoubleChevronRight8' }} />;
        }

        renderPagination =
          <div className={styles.searchPagination}>
              {prevPageEl}
              {nextPageEl}
          </div>;
      }

      renderWebPartContent =
        <React.Fragment>
            {renderOverlay}
            {renderSearchBox}
            {renderSearchResultTemplate}
            {renderPagination}
        </React.Fragment>;
    }

    // Error Message
    if (hasError) {
      renderWebPartContent = <MessageBar messageBarType={MessageBarType.error}>{errorMessage}</MessageBar>;
    }

    return (
      <div style={{backgroundColor: semanticColors.bodyBackground}}>
        <div className={styles.peopleSearchWebPart}>
          {renderWebPartTitle}
          {renderShimmerElements ? renderShimmerElements : renderWebPartContent}
        </div>
      </div>
    );
  }
Example #11
Source File: Options.tsx    From hypertrons-crx with Apache License 2.0 4 votes vote down vote up
Options: React.FC = () => {
  const [settings, setSettings] = useState(new Settings());
  const [metaData, setMetaData] = useState(new MetaData());
  const [inited, setInited] = useState(false);
  const [version, setVersion] = useState('0.0.0');
  const [checkingUpdate, setCheckingUpdate] = useState(false);
  const [token, setToken] = useState('');
  const [checkingToken, setCheckingToken] = useState(false);
  const [showDialogToken, setShowDialogToken] = useState(false);
  const [showDialogTokenError, setShowDialogTokenError] = useState(false);
  const [showDialogNotification, setShowDialogNotification] = useState(false);
  const [notificationId, setNotificationId] = useState(0);
  const [notification, setNotification] = useState('');
  const [updateStatus, setUpdateStatus] = useState(UpdateStatus.undefine);
  const [updateUrl, setUpdateUrl] = useState(
    'https://github.com/hypertrons/hypertrons-crx/releases'
  );
  const tokenCurrent = metaData.token;

  const graphOptions: IChoiceGroupOption[] = [
    {
      key: 'antv',
      text: 'Antv',
    },
    {
      key: 'echarts',
      text: 'Echarts',
    },
  ];

  const locale = settings.locale;
  const localeOptions: IChoiceGroupOption[] = [
    {
      key: 'en',
      text: 'English',
    },
    {
      key: 'zh_CN',
      text: '简体中文 (Simplified Chinese)',
    },
  ];

  useEffect(() => {
    const initMetaData = async () => {
      const tempMetaData = await loadMetaData();
      setMetaData(tempMetaData);
      if (tempMetaData.token !== '') {
        setToken(tempMetaData.token);
      }
      const notificationInformation = await getNotificationInformation();
      if (
        notificationInformation.is_published &&
        tempMetaData.idLastNotication < notificationInformation.id
      ) {
        if (locale === 'zh_CN') {
          setNotification(notificationInformation.content.zh);
        } else {
          setNotification(notificationInformation.content.en);
        }
        setNotificationId(notificationInformation.id);
        setShowDialogNotification(true);
      }
    };
    if (!inited) {
      initMetaData();
    }
  }, [inited, locale, metaData]);

  useEffect(() => {
    const initSettings = async () => {
      const temp = await loadSettings();
      setSettings(temp);
      setInited(true);
    };
    if (!inited) {
      initSettings();
    }
  }, [inited, settings]);

  const getVersion = async () => {
    let version = (await chrome.management.getSelf()).version;
    setVersion(version);
  };

  useEffect(() => {
    getVersion();
  }, [version]);

  const saveSettings = async (settings: Settings) => {
    setSettings(settings);
    await chromeSet('settings', settings.toJson());
  };

  const checkUpdateManually = async () => {
    setUpdateStatus(UpdateStatus.undefine);
    setCheckingUpdate(true);
    const [currentVersion, latestVersion, updateUrl] = await checkUpdate();
    if (compareVersion(currentVersion, latestVersion) === -1) {
      setUpdateUrl(updateUrl);
      setUpdateStatus(UpdateStatus.yes);
    } else {
      setUpdateStatus(UpdateStatus.no);
    }
    setCheckingUpdate(false);
  };

  if (!inited) {
    return <div />;
  }

  return (
    <Stack>
      {showDialogNotification && (
        <Dialog
          hidden={!showDialogNotification}
          onDismiss={() => {
            setShowDialogNotification(false);
          }}
          dialogContentProps={{
            type: DialogType.normal,
            title: getMessageByLocale(
              'global_notificationTitle',
              settings.locale
            ),
          }}
          modalProps={{
            isBlocking: true,
          }}
        >
          <Text variant="mediumPlus">{notification}</Text>
          <DialogFooter>
            <DefaultButton
              onClick={() => {
                setShowDialogNotification(false);
              }}
            >
              {getMessageByLocale('global_btn_ok', settings.locale)}
            </DefaultButton>
            <PrimaryButton
              onClick={async () => {
                metaData.idLastNotication = notificationId;
                setMetaData(metaData);
                await chromeSet('meta_data', metaData.toJson());
                setShowDialogNotification(false);
              }}
            >
              {getMessageByLocale('global_btn_disable', settings.locale)}
            </PrimaryButton>
          </DialogFooter>
        </Dialog>
      )}
      {showDialogToken && (
        <Dialog
          hidden={!showDialogToken}
          onDismiss={() => {
            setShowDialogToken(false);
          }}
          dialogContentProps={{
            type: DialogType.normal,
            title: getMessageByLocale(
              'options_token_dialog_title',
              settings.locale
            ),
          }}
          modalProps={{
            isBlocking: true,
          }}
        >
          <p style={{ fontSize: 14, color: '#6a737d', margin: 5 }}>
            {getMessageByLocale(
              'options_token_dialog_description',
              settings.locale
            )}
          </p>
          <Stack horizontal style={{ fontSize: 16, margin: 5 }}>
            <Link
              href="https://github.com/settings/tokens/new"
              target="_blank"
              underline
            >
              {getMessageByLocale(
                'options_token_dialog_message',
                settings.locale
              )}
            </Link>
          </Stack>
          {checkingToken && (
            <Spinner
              label={getMessageByLocale(
                'options_token_dialog_checking',
                settings.locale
              )}
            />
          )}
          {showDialogTokenError && (
            <MessageBar messageBarType={MessageBarType.error}>
              {getMessageByLocale(
                'options_token_dialog_error',
                settings.locale
              )}
            </MessageBar>
          )}
          <Stack
            horizontal
            horizontalAlign="space-around"
            verticalAlign="end"
            style={{ margin: '10px' }}
            tokens={{
              childrenGap: 15,
            }}
          >
            <TextField
              style={{ width: '200px' }}
              defaultValue={token}
              onChange={(e, value) => {
                if (value) {
                  setShowDialogTokenError(false);
                  setToken(value);
                }
              }}
            />
            <PrimaryButton
              disabled={checkingToken}
              onClick={async () => {
                setCheckingToken(true);
                const result = await checkIsTokenAvailabe(token);
                setCheckingToken(false);
                if ('id' in result) {
                  metaData.token = token;
                  metaData.avatar = result['avatar_url'];
                  metaData.name = result['name'];
                  metaData.id = result['id'];
                  setMetaData(metaData);
                  await chromeSet('meta_data', metaData.toJson());
                  setShowDialogToken(false);
                } else {
                  setShowDialogTokenError(true);
                }
              }}
            >
              {getMessageByLocale('global_btn_ok', settings.locale)}
            </PrimaryButton>
          </Stack>
          {tokenCurrent !== '' && (
            <DefaultButton
              onClick={async () => {
                metaData.token = '';
                metaData.avatar = '';
                metaData.name = '';
                metaData.id = '';
                setMetaData(metaData);
                await chromeSet('meta_data', metaData.toJson());
                setShowDialogToken(false);
              }}
              style={{
                width: 120,
              }}
            >
              {getMessageByLocale('options_token_btn_rmToken', settings.locale)}
            </DefaultButton>
          )}
        </Dialog>
      )}
      <Stack horizontalAlign="center" style={{ paddingBottom: '10px' }}>
        <h1>PERCEPTOR</h1>
        <sub>{`version ${version}`}</sub>
      </Stack>
      <Stack
        horizontalAlign="center"
        tokens={{
          childrenGap: 30,
        }}
      >
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_enable_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_enable_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack
            style={{ margin: '10px 25px' }}
            tokens={{
              childrenGap: 10,
            }}
          >
            <p>
              {getMessageByLocale('options_enable_toolTip', settings.locale)}.
            </p>
            <Toggle
              label={getMessageByLocale(
                'options_enable_toggle_autoCheck',
                settings.locale
              )}
              defaultChecked={settings.isEnabled}
              onText={getMessageByLocale(
                'global_toggle_onText',
                settings.locale
              )}
              offText={getMessageByLocale(
                'global_toggle_offText',
                settings.locale
              )}
              onChange={async (e, checked) => {
                settings.isEnabled = checked;
                await saveSettings(settings);
              }}
            />
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_locale_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_locale_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack style={{ margin: '10px 25px' }}>
            <p>
              {getMessageByLocale('options_locale_toolTip', settings.locale)} :
            </p>
            <ChoiceGroup
              defaultSelectedKey={settings.locale}
              options={localeOptions}
              onChange={async (e, option: any) => {
                settings.locale = option.key;
                await saveSettings(settings);
              }}
            />
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_components_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale(
                  'options_components_title',
                  settings.locale
                )}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack
            style={{ margin: '10px 25px' }}
            tokens={{
              childrenGap: 10,
            }}
          >
            <p>
              {getMessageByLocale(
                'options_components_toolTip',
                settings.locale
              )}{' '}
              :
            </p>
            <Checkbox
              label={getMessageByLocale(
                'component_developerCollabrationNetwork_title',
                settings.locale
              )}
              defaultChecked={settings.developerNetwork}
              onChange={async (e, checked) => {
                settings.developerNetwork = checked;
                await saveSettings(settings);
              }}
            />
            <Checkbox
              label={getMessageByLocale(
                'component_projectCorrelationNetwork_title',
                settings.locale
              )}
              defaultChecked={settings.projectNetwork}
              onChange={async (e, checked) => {
                settings.projectNetwork = checked;
                await saveSettings(settings);
              }}
            />
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_graphType_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_graphType_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack style={{ margin: '10px 25px' }}>
            <p>
              {getMessageByLocale('options_graphType_toolTip', settings.locale)}{' '}
              :
            </p>
            <ChoiceGroup
              defaultSelectedKey={settings.graphType}
              options={graphOptions}
              onChange={async (e, option: any) => {
                settings.graphType = option.key as GraphType;
                await saveSettings(settings);
              }}
            />
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_update_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_update_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack
            style={{ margin: '10px 25px' }}
            tokens={{
              childrenGap: 10,
            }}
          >
            <p>
              {getMessageByLocale('options_update_toolTip', settings.locale)}.
            </p>
            <Toggle
              label={getMessageByLocale(
                'options_update_toggle_autoCheck',
                settings.locale
              )}
              defaultChecked={settings.checkForUpdates}
              onText={getMessageByLocale(
                'global_toggle_onText',
                settings.locale
              )}
              offText={getMessageByLocale(
                'global_toggle_offText',
                settings.locale
              )}
              onChange={async (e, checked) => {
                settings.checkForUpdates = checked;
                await saveSettings(settings);
              }}
            />
            {checkingUpdate && (
              <Stack horizontalAlign="start">
                <Spinner
                  label={getMessageByLocale(
                    'options_update_checking',
                    settings.locale
                  )}
                />
              </Stack>
            )}
            {updateStatus === UpdateStatus.yes && (
              <MessageBar
                messageBarType={MessageBarType.success}
                isMultiline={false}
              >
                {getMessageByLocale(
                  'options_update_btn_updateStatusYes',
                  settings.locale
                )}
                <Link href={updateUrl} target="_blank" underline>
                  {getMessageByLocale(
                    'options_update_btn_getUpdate',
                    settings.locale
                  )}
                </Link>
              </MessageBar>
            )}
            {updateStatus === UpdateStatus.no && (
              <MessageBar
                messageBarType={MessageBarType.info}
                isMultiline={false}
              >
                {getMessageByLocale(
                  'options_update_btn_updateStatusNo',
                  settings.locale
                )}
              </MessageBar>
            )}
            <DefaultButton
              style={{
                width: 120,
              }}
              disabled={checkingUpdate}
              onClick={async () => {
                await checkUpdateManually();
              }}
            >
              {getMessageByLocale(
                'options_update_btn_checkUpdate',
                settings.locale
              )}
            </DefaultButton>
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_token_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_token_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack
            style={{ margin: '10px 25px' }}
            tokens={{
              childrenGap: 10,
            }}
          >
            <p>
              {getMessageByLocale('options_token_toolTip', settings.locale)} :
            </p>
            {tokenCurrent !== '' && (
              <Stack
                horizontal
                verticalAlign="center"
                style={{
                  margin: '5px',
                  padding: '3px',
                  width: '300px',
                  boxShadow: '4px 4px 10px rgba(0, 0, 0, 0.2)',
                }}
                tokens={{
                  childrenGap: 5,
                }}
              >
                <Image
                  width={75}
                  height={75}
                  src={metaData.avatar}
                  imageFit={ImageFit.centerCover}
                />
                <Text
                  variant="large"
                  style={{
                    marginLeft: 25,
                    maxWidth: 200,
                    wordWrap: 'break-word',
                  }}
                >
                  {metaData.name}
                </Text>
              </Stack>
            )}
            <DefaultButton
              onClick={() => {
                setShowDialogToken(true);
              }}
              style={{
                width: 120,
              }}
            >
              {getMessageByLocale(
                'options_token_btn_setToken',
                settings.locale
              )}
            </DefaultButton>
          </Stack>
        </Stack.Item>
        <Stack.Item className="Box">
          <TooltipHost
            content={getMessageByLocale(
              'options_about_toolTip',
              settings.locale
            )}
          >
            <Stack.Item className="Box-header">
              <h2 className="Box-title">
                {getMessageByLocale('options_about_title', settings.locale)}
              </h2>
            </Stack.Item>
          </TooltipHost>
          <Stack style={{ margin: '10px 25px' }}>
            <p>
              {getMessageByLocale('options_about_description', settings.locale)}
            </p>
            <p>
              {getMessageByLocale(
                'options_about_description_website',
                settings.locale
              )}
            </p>
            <Link href={HYPERTRONS_CRX_WEBSITE} target="_blank" underline>
              {HYPERTRONS_CRX_WEBSITE}
            </Link>
          </Stack>
        </Stack.Item>
      </Stack>
    </Stack>
  );
}
Example #12
Source File: GenericObjectEditor.tsx    From sp-site-designs-studio with MIT License 4 votes vote down vote up
export function PropertyEditor(props: IPropertyEditorProps) {
    let { schema,
        label,
        readonly,
        required,
        value,
        onChange } = props;

    const onDropdownChange = ((ev: any, v: IDropdownOption) => {
        onChange(v.key);
    });

    const onNumberInputChange = ((ev: any, v: any) => {
        if (typeof (v) === "number") {
            onChange(v);
        } else {
            const number = parseFloat(v as string);
            onChange(number);
        }
    });

    const onInputChange = ((ev: any, v: any) => {
        onChange(v);
    });

    if (schema.enum) {
        if (schema.enum.length > 1 && !readonly) {
            return (
                <Dropdown
                    required={required}
                    label={label}
                    selectedKey={value}
                    options={schema.enum.map((p) => ({ key: p, text: p }))}
                    onChange={onDropdownChange}
                />
            );
        } else {
            return (
                <TextField
                    label={label}
                    value={value}
                    readOnly={true}
                    required={required}
                    onChange={onInputChange}
                />
            );
        }
    } else {
        switch (schema.type) {
            case 'boolean':
                return (
                    <Toggle
                        label={label}
                        checked={value as boolean}
                        disabled={readonly}
                        onChange={onInputChange}
                    />
                );
            case 'array':
                return <>
                    <Label>{label}</Label>
                    <GenericArrayEditor
                        object={value}
                        schema={schema.items}
                        onObjectChanged={onChange} />
                </>;
            case 'object': // TODO If object is a simple dictionary (key/non-complex object values) => Display a custom control
                // return <GenericObjectEditor
                //     object={value}
                //     schema={schema}
                //     onObjectChanged={onChange}
                // />;
                return <div>
                    <Label>{label}</Label>
                    <MessageBar messageBarType={MessageBarType.info}>{`This value of this property must be a complex object. Please use code editor to edit it`}</MessageBar>
                </div>;
            case 'number':
                return (
                    <TextField
                        required={required}
                        label={label}
                        value={value}
                        readOnly={readonly}
                        onChange={onNumberInputChange}
                    />
                );
            case 'string':
            default:
                return (
                    <TextField
                        required={required}
                        label={label}
                        value={value}
                        readOnly={readonly}
                        onChange={onInputChange}
                    />
                );
        }
    }
}
Example #13
Source File: NewSiteScriptPanel.tsx    From sp-site-designs-studio with MIT License 4 votes vote down vote up
NewSiteScriptPanel = (props: INewSiteScriptPanelProps) => {

    const [appContext, action] = useAppContext<IApplicationState, ActionType>();
    // Get services instances
    const siteScriptSchemaService = appContext.serviceScope.consume(SiteScriptSchemaServiceKey);
    const siteDesignsService = appContext.serviceScope.consume(SiteDesignsServiceKey);
    const [needsArguments, setNeedsArguments] = useState<boolean>(false);
    const [creationArgs, setCreationArgs] = useState<ICreateArgs>({ from: "BLANK" });
    const [fromWebArgs, setFromWebArgs] = useState<IGetSiteScriptFromWebOptions>(getDefaultFromWebArgs());
    const [selectedSample, setSelectedSample] = useState<ISiteScriptSample>(null);

    useEffect(() => {
        setCreationArgs({ from: "BLANK" });
        setNeedsArguments(false);
        setFromWebArgs(getDefaultFromWebArgs());
    }, [props.isOpen]);

    const onCancel = () => {
        if (props.onCancel) {
            props.onCancel();
        }
    };

    const onScriptAdded = async () => {
        try {
            let newSiteScriptContent: ISiteScriptContent = null;
            let fromExistingResult: IGetSiteScriptFromExistingResourceResult = null;
            switch (creationArgs.from) {
                case "BLANK":
                    newSiteScriptContent = siteScriptSchemaService.getNewSiteScript();
                    break;
                case "WEB":
                    fromExistingResult = await siteDesignsService.getSiteScriptFromWeb(creationArgs.webUrl, fromWebArgs);
                    newSiteScriptContent = fromExistingResult.JSON;
                    break;
                case "LIST":
                    fromExistingResult = await siteDesignsService.getSiteScriptFromList(creationArgs.listUrl);
                    newSiteScriptContent = fromExistingResult.JSON;
                    break;
                case "SAMPLE":
                    if (selectedSample) {
                        try {
                            const jsonWithIgnoredComments = selectedSample.jsonContent.replace(/\/\*(.*)\*\//g,'');
                            newSiteScriptContent = JSON.parse(jsonWithIgnoredComments);
                        } catch (error) {
                            action("SET_USER_MESSAGE", {
                                userMessage: {
                                    message: "The JSON of this site script sample is unfortunately invalid... Please reach out to the maintainer to report this issue",
                                    messageType: MessageBarType.error
                                }
                            });
                        }

                    } else {
                        console.error("The sample JSON is not defined.");
                    }
                    break;
            }

            const siteScript: ISiteScript = {
                Id: null,
                Title: null,
                Description: null,
                Version: 1,
                Content: newSiteScriptContent
            };
            action("EDIT_SITE_SCRIPT", { siteScript } as IEditSiteScriptActionArgs);
        } catch (error) {
            console.error(error);
        }
    };

    const onChoiceClick = (createArgs: ICreateArgs) => {
        setCreationArgs(createArgs);
        switch (createArgs.from) {
            case "BLANK":
                onScriptAdded();
                break;
            case "LIST":
                setNeedsArguments(true);
                break;
            case "WEB":
                setNeedsArguments(true);
                break;
            case "SAMPLE":
                setNeedsArguments(true);
                break;
        }
    };

    const renderFromWebArgumentsForm = () => {
        return <Stack tokens={{ childrenGap: 8 }}>
            <SitePicker label="Site" onSiteSelected={webUrl => {
                setCreationArgs({ ...creationArgs, webUrl });
            }} serviceScope={appContext.serviceScope} />
            <ListPicker serviceScope={appContext.serviceScope}
                webUrl={creationArgs.webUrl}
                label="Include lists"
                multiselect
                onListsSelected={(includeLists) => setFromWebArgs({ ...fromWebArgs, includeLists: !includeLists ? [] : includeLists.map(l => l.webRelativeUrl) })}
            />
            <div className={styles.toggleRow}>
                <div className={styles.column8}>Include Branding</div>
                <div className={styles.column4}>
                    <Toggle checked={fromWebArgs && fromWebArgs.includeBranding} onChange={(_, includeBranding) => setFromWebArgs({ ...fromWebArgs, includeBranding })} />
                </div>
            </div>
            <div className={styles.toggleRow}>
                <div className={styles.column8}>Include Regional settings</div>
                <div className={styles.column4}>
                    <Toggle checked={fromWebArgs && fromWebArgs.includeRegionalSettings} onChange={(_, includeRegionalSettings) => setFromWebArgs({ ...fromWebArgs, includeRegionalSettings })} />
                </div>
            </div>
            <div className={styles.toggleRow}>
                <div className={styles.column8}>Include Site external sharing capability</div>
                <div className={styles.column4}>
                    <Toggle checked={fromWebArgs && fromWebArgs.includeSiteExternalSharingCapability} onChange={(_, includeSiteExternalSharingCapability) => setFromWebArgs({ ...fromWebArgs, includeSiteExternalSharingCapability })} />
                </div>
            </div>
            <div className={styles.toggleRow}>
                <div className={styles.column8}>Include theme</div>
                <div className={styles.column4}>
                    <Toggle checked={fromWebArgs && fromWebArgs.includeTheme} onChange={(_, includeTheme) => setFromWebArgs({ ...fromWebArgs, includeTheme })} />
                </div>
            </div>
            <div className={styles.toggleRow}>
                <div className={styles.column8}>Include links to exported items</div>
                <div className={styles.column4}>
                    <Toggle checked={fromWebArgs && fromWebArgs.includeLinksToExportedItems} onChange={(_, includeLinksToExportedItems) => setFromWebArgs({ ...fromWebArgs, includeLinksToExportedItems })} />
                </div>
            </div>
        </Stack>;
    };

    const renderFromListArgumentsForm = () => {
        return <Stack tokens={{ childrenGap: 8 }}>
            <SitePicker label="Site" onSiteSelected={webUrl => {
                setCreationArgs({ ...creationArgs, webUrl });
            }} serviceScope={appContext.serviceScope} />
            <ListPicker serviceScope={appContext.serviceScope}
                webUrl={creationArgs.webUrl}
                label="List"
                onListSelected={(list) => setCreationArgs({ ...creationArgs, listUrl: list && list.url })}
            />
        </Stack>;
    };

    const renderSamplePicker = () => {
        return <SiteScriptSamplePicker
            selectedSample={selectedSample}
            onSelectedSample={setSelectedSample} />;
    };

    const renderArgumentsForm = () => {
        if (!needsArguments) {
            return null;
        }

        switch (creationArgs.from) {
            case "LIST":
                return renderFromListArgumentsForm();
            case "WEB":
                return renderFromWebArgumentsForm();
            case "SAMPLE":
                return renderSamplePicker();
            default:
                return null;
        }
    };

    const validateArguments = () => {
        if (!creationArgs) {
            return false;
        }

        switch (creationArgs.from) {
            case "SAMPLE":
                return !!(selectedSample && selectedSample.jsonContent);
            default:
                return true;
        }
    };

    const getPanelHeaderText = () => {
        if (!needsArguments) {
            return "Add a new Site Script";
        }

        switch (creationArgs.from) {
            case "LIST":
                return "Add a new Site Script from existing list";
            case "WEB":
                return "Add a new Site Script from existing site";
            case "SAMPLE":
                return "Add a new Site Script from samples";
            default:
                return "";
        }
    };

    const panelType = creationArgs.from == "SAMPLE" ? PanelType.extraLarge : PanelType.smallFixedFar;
    return <Panel type={panelType}
        headerText={getPanelHeaderText()}
        isOpen={props.isOpen}
        onDismiss={onCancel}
        onRenderFooterContent={() => needsArguments && <div className={styles.panelFooter}>
            <Stack horizontalAlign="end" horizontal tokens={{ childrenGap: 25 }}>
                <PrimaryButton text="Add Site Script" onClick={onScriptAdded} disabled={!validateArguments()} />
                <DefaultButton text="Cancel" onClick={onCancel} />
            </Stack>
        </div>}>
        <div className={styles.NewSiteScriptPanel}>
            {!needsArguments && <Stack tokens={{ childrenGap: 20 }}>
                <CompoundButton
                    iconProps={{ iconName: "PageAdd" }}
                    text="Blank"
                    secondaryText="Create a new blank Site Script"
                    onClick={() => onChoiceClick({ from: "BLANK" })}
                />
                <CompoundButton
                    iconProps={{ iconName: "SharepointLogoInverse" }}
                    text="From Site"
                    secondaryText="Create a new Site Script from an existing site"
                    onClick={() => onChoiceClick({ from: "WEB" })}
                />
                <CompoundButton
                    iconProps={{ iconName: "PageList" }}
                    text="From List"
                    secondaryText="Create a new Site Script from an existing list"
                    onClick={() => onChoiceClick({ from: "LIST" })}
                />
                <CompoundButton
                    iconProps={{ iconName: "ProductCatalog" }}
                    text="From Sample"
                    secondaryText="Create a new Site Script from a sample"
                    onClick={() => onChoiceClick({ from: "SAMPLE" })}
                />
            </Stack>}
            {renderArgumentsForm()}
        </div>
    </Panel>;
}
Example #14
Source File: SiteScriptSamplePicker.tsx    From sp-site-designs-studio with MIT License 4 votes vote down vote up
SiteScriptSamplePicker = (props: ISiteScriptSamplePickerProps) => {
    const [appContext, execute] = useAppContext<IApplicationState, any>();
    const samplesService = appContext.serviceScope.consume(SiteScriptSamplesServiceKey);

    const [repo, setRepo] = useState<{
        availableRepositories: ISiteScriptSamplesRepository[];
        currentRepository: ISiteScriptSamplesRepository;
    }>({
        availableRepositories: [],
        currentRepository: null
    });
    const [selectedSampleKey, setSelectedSampleKey] = useState<string>(null);
    const [availableSamples, setAvailableSamples] = useState<ISiteScriptSample[]>([]);
    const [isLoadingAllSamples, setIsLoadingAllSamples] = useState<boolean>(true);
    const [isLoadingSampleDetails, setIsLoadingSampleDetails] = useState<boolean>(false);
    const [searchCriteria, setSearchCriteria] = useState<string>('');

    const getSamples = async (repository: ISiteScriptSamplesRepository) => {
        repository = repository || repo.currentRepository;
        return repository ? await samplesService.getSamples(repository) : [];
    };

    const initialLoad = async () => {
        try {
            const availableRepositories = await samplesService.getAvailableRepositories();
            // Select the first available repository and get the samples from it
            const currentRepository = availableRepositories && availableRepositories.length > 0 ? availableRepositories[0] : null;
            setRepo({ currentRepository, availableRepositories });

            const foundSamples = await getSamples(currentRepository);
            setAvailableSamples(foundSamples);
            setIsLoadingAllSamples(false);
        } catch (error) {
            setIsLoadingAllSamples(false);
            // TODO Determine the reason of the error
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: "All samples cannot be loaded...",
                    userMessageType: MessageBarType.error
                }
            });
        }

    };

    const justMounted = useRef<boolean>(false);
    useEffect(() => {
        initialLoad();
        justMounted.current = true;
    }, []);

    const loadSampleData = async (sample: ISiteScriptSample) => {
        try {
            setIsLoadingSampleDetails(true);
            const loadedSample = await samplesService.getSample(sample);
            setIsLoadingSampleDetails(false);
            return loadedSample;
        } catch (error) {
            setIsLoadingSampleDetails(false);
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: "Sample cannot be loaded...",
                    userMessageType: MessageBarType.error
                }
            });
            return null;
        }
    };

    const selectSample = async (sample: ISiteScriptSample) => {
        setSelectedSampleKey(sample.key);
        const loadedSample = await loadSampleData(sample);
        if (props.onSelectedSample) {
            props.onSelectedSample(loadedSample);
        }
    };

    const renderSampleItem = (item: ISelectableSiteScriptSample, finalSize: ISize, isCompact: boolean): JSX.Element => {

        return <div
            data-is-focusable={true}
            role="listitem"
            aria-label={item.sample.key}
            className={item.selected ? styles.selectedSample : ''}
        >
            <DocumentCard
                type={isCompact ? DocumentCardType.compact : DocumentCardType.normal}
                onClick={(ev: React.SyntheticEvent<HTMLElement>) => selectSample(item.sample)}>
                <div className={styles.iconBox}>
                    <div className={styles.icon}>
                        <Icon iconName="Script" />
                    </div>
                </div>
                <DocumentCardDetails>
                    <DocumentCardTitle
                        title={item.sample.key}
                        shouldTruncate={true}
                    />
                </DocumentCardDetails>
            </DocumentCard>
        </div>;
    };

    return <div className={styles.row}>
        {isLoadingAllSamples && <div className={styles.column}>
            <ProgressIndicator label="Loading..." />
        </div>}
        {!isLoadingAllSamples && <div className={`${selectedSampleKey ? styles.column6 : styles.column}`}>
            <Stack tokens={{ childrenGap: 3 }}>
                <SearchBox underlined value={searchCriteria} onChange={setSearchCriteria} placeholder={"Search a sample..."} />
                <div className={styles.sampleGallery}>
                    <GridLayout
                        ariaLabel="List of Site Scripts samples."
                        items={availableSamples
                            .filter(s => !searchCriteria || s.key.toLowerCase().indexOf(searchCriteria.toLowerCase()) > -1)
                            .map(s => ({ sample: s, selected: s.key == selectedSampleKey }))}
                        onRenderGridItem={renderSampleItem}
                    />
                </div>
            </Stack>
        </div>}
        {selectedSampleKey && <div className={`${styles.column6}`}>
            <div key="sample-readme">
                {isLoadingSampleDetails ? <ProgressIndicator label="Loading..." />
                    : <div>
                        {props.selectedSample &&
                            <div>
                                <Pivot>
                                    <PivotItem itemKey="readme" headerText="README">
                                        {props.selectedSample.readmeHtml
                                            ? <div className={styles.readmeViewer} dangerouslySetInnerHTML={{ __html: props.selectedSample.readmeHtml }} />
                                            : <MessageBar messageBarType={MessageBarType.warning}>
                                                {"README of this sample cannot be displayed..."}
                                            </MessageBar>}
                                    </PivotItem>
                                    <PivotItem itemKey="json" headerText="JSON">
                                        {props.selectedSample.jsonContent
                                            ? <div>
                                                {props.selectedSample.hasPreprocessedJsonContent && <MessageBar messageBarType={MessageBarType.warning}>
                                                    {"The JSON of this sample has been preprocessed..."}<br />
                                                    {`Visit`}<a href={props.selectedSample.webSite} target="_blank">{`this page`}</a> {` to view the original sample`}
                                                </MessageBar>}
                                                <CodeEditor
                                                    height="70vh"
                                                    language="json"
                                                    options={{
                                                        readOnly: true,
                                                        folding: true,
                                                        renderIndentGuides: true,
                                                        minimap: {
                                                            enabled: false
                                                        }
                                                    }}
                                                    value={props.selectedSample.jsonContent}
                                                />
                                            </div>
                                            : <MessageBar messageBarType={MessageBarType.warning}>
                                                {"A usable JSON file cannot be found in this sample..."}<br />
                                                {`Visit`}<a href={props.selectedSample.webSite} target="_blank">{`this page`}</a> {` to explore this sample`}
                                            </MessageBar>}
                                    </PivotItem>
                                </Pivot>
                            </div>
                        }
                    </div>}
            </div>
        </div>}
    </div>;
}
Example #15
Source File: SiteScriptEditor.tsx    From sp-site-designs-studio with MIT License 3 votes vote down vote up
SiteScriptEditor = (props: ISiteScriptEditorProps) => {
    useTraceUpdate('SiteScriptEditor', props);
    const [appContext, execute] = useAppContext<IApplicationState, ActionType>();

    // Get service references
    const siteDesignsService = appContext.serviceScope.consume(SiteDesignsServiceKey);
    const siteScriptSchemaService = appContext.serviceScope.consume(SiteScriptSchemaServiceKey);
    const exportService = appContext.serviceScope.consume(ExportServiceKey);

    const [state, dispatchState] = useReducer(SiteScriptEditorReducer, {
        siteScriptMetadata: null,
        siteScriptContent: null,
        currentExportPackage: null,
        currentExportType: "json",
        isExportUIVisible: false,
        isSaving: false,
        isAssociatingToSiteDesign: false,
        isValidCode: true,
        updatedContentFrom: null
    });
    const { siteScriptMetadata,
        siteScriptContent,
        isValidCode,
        isExportUIVisible,
        currentExportType,
        currentExportPackage,
        isAssociatingToSiteDesign,
        isSaving } = state;

    // Use refs
    const codeEditorRef = useRef<any>();
    const titleFieldRef = useRef<any>();
    const selectedSiteDesignRef = useRef<string>();


    const setLoading = (loading: boolean) => {
        execute("SET_LOADING", { loading });
    };
    // Use Effects
    useEffect(() => {
        if (!props.siteScript.Id) {
            dispatchState({ type: "SET_SITE_SCRIPT", siteScript: props.siteScript });

            if (titleFieldRef.current) {
                titleFieldRef.current.focus();
            }
            return;
        }

        setLoading(true);
        console.debug("Loading site script...", props.siteScript.Id);
        siteDesignsService.getSiteScript(props.siteScript.Id).then(loadedSiteScript => {
            dispatchState({ type: "SET_SITE_SCRIPT", siteScript: loadedSiteScript });
            console.debug("Loaded: ", loadedSiteScript);
        }).catch(error => {
            console.error(`The Site Script ${props.siteScript.Id} could not be loaded`, error);
        }).then(() => {
            setLoading(false);
        });
    }, [props.siteScript]);

    const onTitleChanged = (ev: any, title: string) => {
        const siteScript = { ...siteScriptMetadata, Title: title };
        dispatchState({ type: "UPDATE_SITE_SCRIPT_METADATA", siteScript });
    };

    let currentDescription = useRef<string>(siteScriptMetadata && siteScriptMetadata.Description);
    const onDescriptionChanging = (ev: any, description: string) => {
        currentDescription.current = description;
    };

    const onDescriptionInputBlur = (ev: any) => {
        const siteScript = { ...siteScriptMetadata, Description: currentDescription.current };
        dispatchState({ type: "UPDATE_SITE_SCRIPT_METADATA", siteScript });
    };

    const onVersionChanged = (ev: any, version: string) => {
        const versionInt = parseInt(version);
        if (!isNaN(versionInt)) {
            const siteScript = { ...siteScriptMetadata, Version: versionInt };
            dispatchState({ type: "UPDATE_SITE_SCRIPT_METADATA", siteScript });
        }
    };

    const onSiteScriptContentUpdatedFromUI = (content: ISiteScriptContent) => {
        dispatchState({ type: "UPDATE_SITE_SCRIPT_CONTENT", content, from: "UI" });
    };

    const onSave = async () => {
        dispatchState({ type: "SET_ISSAVING", isSaving: true });
        try {
            const isNewSiteScript = !siteScriptMetadata.Id;
            const toSave: ISiteScript = { ...siteScriptMetadata, Content: state.siteScriptContent };
            const updated = await siteDesignsService.saveSiteScript(toSave);
            const refreshedSiteScripts = await siteDesignsService.getSiteScripts();
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: `${siteScriptMetadata.Title} has been successfully saved.`,
                    messageType: MessageBarType.success
                }
            } as ISetUserMessageArgs);
            execute("SET_ALL_AVAILABLE_SITE_SCRIPTS", { siteScripts: refreshedSiteScripts } as ISetAllAvailableSiteScripts);
            dispatchState({ type: "SET_SITE_SCRIPT", siteScript: updated });
            if (isNewSiteScript) {
                // Ask if the new Site Script should be associated to a Site Design
                if (await Confirm.show({
                    title: `Associate to Site Design`,
                    message: `Do you want to associate the new ${(siteScriptMetadata && siteScriptMetadata.Title)} to a Site Design ?`,
                    cancelLabel: 'No',
                    okLabel: 'Yes'
                })) {
                    dispatchState({ type: "SET_ISASSOCIATINGTOSITEDESIGN", isAssociatingToSiteDesign: true });
                }
            }
        } catch (error) {
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: `${siteScriptMetadata.Title} could not be saved. Please make sure you have SharePoint administrator privileges...`,
                    messageType: MessageBarType.error
                }
            } as ISetUserMessageArgs);
            console.error(error);
        }
        dispatchState({ type: "SET_ISSAVING", isSaving: false });

    };

    const onDelete = async () => {
        if (!await Confirm.show({
            title: `Delete Site Script`,
            message: `Are you sure you want to delete ${(siteScriptMetadata && siteScriptMetadata.Title) || "this Site Script"} ?`
        })) {
            return;
        }

        dispatchState({ type: "SET_ISSAVING", isSaving: true });
        try {
            await siteDesignsService.deleteSiteScript(siteScriptMetadata);
            const refreshedSiteScripts = await siteDesignsService.getSiteScripts();
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: `${siteScriptMetadata.Title} has been successfully deleted.`,
                    messageType: MessageBarType.success
                }
            } as ISetUserMessageArgs);
            execute("SET_ALL_AVAILABLE_SITE_SCRIPTS", { siteScripts: refreshedSiteScripts } as ISetAllAvailableSiteScripts);
            execute("GO_TO", { page: "SiteScriptsList" } as IGoToActionArgs);
        } catch (error) {
            execute("SET_USER_MESSAGE", {
                userMessage: {
                    message: `${siteScriptMetadata.Title} could not be deleted. Please make sure you have SharePoint administrator privileges...`,
                    messageType: MessageBarType.error
                }
            } as ISetUserMessageArgs);
            console.error(error);
        }
        dispatchState({ type: "SET_ISSAVING", isSaving: false });
    };

    const onAssociateSiteScript = () => {
        if (selectedSiteDesignRef.current != NEW_SITE_DESIGN_KEY) {
            execute("EDIT_SITE_DESIGN", { siteDesign: { Id: selectedSiteDesignRef.current }, additionalSiteScriptIds: [siteScriptMetadata.Id] });
        } else if (selectedSiteDesignRef.current) {
            execute("EDIT_SITE_DESIGN", { siteDesign: createNewSiteDesign(), additionalSiteScriptIds: [siteScriptMetadata.Id] });
        }
    };

    const onExportRequested = (exportType?: ExportType) => {
        const toExport: ISiteScript = { ...siteScriptMetadata, Content: siteScriptContent };
        let exportPromise: Promise<ExportPackage> = null;
        switch (exportType) {
            case "PnPPowershell":
                exportPromise = exportService.generateSiteScriptPnPPowershellExportPackage(toExport);
                break;
            case "PnPTemplate":
                break; // Not yet supported
            case "o365_PS":
                exportPromise = exportService.generateSiteScriptO365CLIExportPackage(toExport, "Powershell");
                break;
            case "o365_Bash":
                exportPromise = exportService.generateSiteScriptO365CLIExportPackage(toExport, "Bash");
                break;
            case "json":
            default:
                exportPromise = exportService.generateSiteScriptJSONExportPackage(toExport);
                break;
        }

        if (exportPromise) {
            exportPromise.then(exportPackage => {
                dispatchState({ type: "SET_EXPORTPACKAGE", exportPackage, exportType });
            });
        }
    };

    let codeUpdateTimeoutHandle: any = null;
    const onCodeChanged = (updatedCode: string) => {
        if (!updatedCode) {
            return;
        }

        if (codeUpdateTimeoutHandle) {
            clearTimeout(codeUpdateTimeoutHandle);
        }

        // if (updatedContentFrom == "UI") {
        //     // Not trigger the change of state if the script content was updated from UI
        //     console.debug("The code has been modified after a change in designer. The event will not be propagated");
        //     dispatchState({ type: "UPDATE_SITE_SCRIPT", siteScript: null, from: "CODE" });
        //     return;
        // }

        codeUpdateTimeoutHandle = setTimeout(() => {
            try {
                const jsonWithIgnoredComments = updatedCode.replace(/\/\*(.*)\*\//g,'');
                if (siteScriptSchemaService.validateSiteScriptJson(jsonWithIgnoredComments)) {
                    const content = JSON.parse(jsonWithIgnoredComments) as ISiteScriptContent;
                    dispatchState({ type: "UPDATE_SITE_SCRIPT_CONTENT", content, isValidCode: true, from: "CODE" });
                } else {
                    dispatchState({ type: "UPDATE_SITE_SCRIPT_CONTENT", content: null, isValidCode: false, from: "CODE" });

                }
            } catch (error) {
                console.warn("Code is not valid site script JSON");
            }
        }, 500);
    };

    const editorDidMount = (_, editor) => {

        const schema = siteScriptSchemaService.getSiteScriptSchema();
        codeEditorRef.current = editor;
        monaco.init().then(monacoApi => {
            monacoApi.languages.json.jsonDefaults.setDiagnosticsOptions({
                schemas: [{
                    uri: 'schema.json',
                    schema
                }],

                validate: true,
                allowComments: false
            });
        }).catch(error => {
            console.error("An error occured while trying to configure code editor");
        });

        const editorModel = editor.getModel();
        console.log("Editor model: ", editorModel);

        editor.onDidChangeModelContent(ev => {
            if (codeEditorRef && codeEditorRef.current) {
                onCodeChanged(codeEditorRef.current.getValue());
            }
        });
    };

    const checkIsValidForSave: () => [boolean, string?] = () => {
        if (!siteScriptMetadata) {
            return [false, "Current Site Script not defined"];
        }

        if (!siteScriptMetadata.Title) {
            return [false, "Please set the title of the Site Script..."];
        }

        if (!isValidCode) {
            return [false, "Please check the validity of the code..."];
        }

        return [true];
    };

    const isLoading = appContext.isLoading;
    const [isValidForSave, validationMessage] = checkIsValidForSave();
    if (!siteScriptMetadata || !siteScriptContent) {
        return null;
    }

    return <div className={styles.SiteScriptEditor}>
        <div className={styles.row}>
            <div className={styles.columnLayout}>
                <div className={styles.row}>
                    <div className={styles.column11}>
                        <TextField
                            styles={{
                                field: {
                                    fontSize: "32px",
                                    lineHeight: "45px",
                                    height: "45px"
                                },
                                root: {
                                    height: "60px",
                                    marginTop: "5px",
                                    marginBottom: "5px"
                                }
                            }}
                            placeholder="Enter the name of the Site Script..."
                            borderless
                            componentRef={titleFieldRef}
                            value={siteScriptMetadata.Title}
                            onChange={onTitleChanged} />
                        {isLoading && <ProgressIndicator />}
                    </div>
                    {!isLoading && <div className={`${styles.column1} ${styles.righted}`}>
                        <Stack horizontal horizontalAlign="end" tokens={{ childrenGap: 15 }}>
                            <CommandButton disabled={isSaving} iconProps={{ iconName: "More" }} menuProps={{
                                items: [
                                    (siteScriptMetadata.Id && {
                                        key: 'deleteScript',
                                        text: 'Delete',
                                        iconProps: { iconName: 'Delete' },
                                        onClick: onDelete
                                    }),
                                    {
                                        key: 'export',
                                        text: 'Export',
                                        iconProps: { iconName: 'Download' },
                                        onClick: () => onExportRequested(),
                                        disabled: !isValidForSave
                                    }
                                ].filter(i => !!i),
                            } as IContextualMenuProps} />
                            <PrimaryButton disabled={isSaving || !isValidForSave} text="Save" iconProps={{ iconName: "Save" }} onClick={() => onSave()} />
                        </Stack>
                    </div>}
                </div>
                <div className={styles.row}>
                    {siteScriptMetadata.Id && <div className={styles.half}>
                        <div className={styles.row}>
                            <div className={styles.column8}>
                                <TextField
                                    label="Id"
                                    readOnly
                                    value={siteScriptMetadata.Id} />
                            </div>
                            <div className={styles.column4}>
                                <TextField
                                    label="Version"
                                    value={siteScriptMetadata.Version.toString()}
                                    onChange={onVersionChanged} />
                            </div>
                        </div>
                    </div>}
                    <div className={styles.half}>
                        <TextField
                            label="Description"
                            value={siteScriptMetadata.Description}
                            multiline={true}
                            rows={2}
                            borderless
                            placeholder="Enter a description for the Site Script..."
                            onChange={onDescriptionChanging}
                            onBlur={onDescriptionInputBlur}
                        />
                    </div>
                </div>
                <div className={styles.row}>
                    <div className={styles.column}>
                        <Label>Actions</Label>
                    </div>
                </div>
                <div className={styles.row}>
                    <div className={styles.designerWorkspace}>
                        <SiteScriptDesigner
                            siteScriptContent={siteScriptContent}
                            onSiteScriptContentUpdated={onSiteScriptContentUpdatedFromUI} />
                    </div>
                    <div className={styles.codeEditorWorkspace}>
                        <CodeEditor
                            height="80vh"
                            language="json"
                            options={{
                                folding: true,
                                renderIndentGuides: true,
                                minimap: {
                                    enabled: false
                                }
                            }}
                            value={toJSON(siteScriptContent)}
                            editorDidMount={editorDidMount}
                        />
                    </div>
                </div>
            </div>
        </div>
        {/* Association to a Site Design */}
        <Panel isOpen={isAssociatingToSiteDesign}
            type={PanelType.smallFixedFar}
            headerText="Associate to a Site Design"
            onRenderFooterContent={(p) => <Stack horizontalAlign="end" horizontal tokens={{ childrenGap: 10 }}>
                <PrimaryButton iconProps={{ iconName: "Check" }} text="Continue" onClick={() => onAssociateSiteScript()} />
                <DefaultButton text="Cancel" onClick={() => dispatchState({ type: "SET_ISASSOCIATINGTOSITEDESIGN", isAssociatingToSiteDesign: false })} /></Stack>}
        >
            <SiteDesignPicker serviceScope={appContext.serviceScope}
                label="Site Design"
                onSiteDesignSelected={(siteDesignId) => {
                    console.log("Selected site design: ", siteDesignId);
                    selectedSiteDesignRef.current = siteDesignId;
                }}
                hasNewSiteDesignOption
                displayPreview />
        </Panel>
        {/* Export options */}
        <Panel isOpen={isExportUIVisible}
            type={PanelType.large}
            headerText="Export Site Script"
            onRenderFooterContent={(p) => <Stack horizontalAlign="end" horizontal tokens={{ childrenGap: 10 }}>
                <PrimaryButton iconProps={{ iconName: "Download" }} text="Download" onClick={() => currentExportPackage && currentExportPackage.download()} />
                <DefaultButton text="Cancel" onClick={() => dispatchState({ type: "SET_EXPORTPACKAGE", exportPackage: null })} /></Stack>}>
            <Pivot
                selectedKey={currentExportType}
                onLinkClick={(item) => onExportRequested(item.props.itemKey as ExportType)}
                headersOnly={true}
            >
                <PivotItem headerText="JSON" itemKey="json" />
                <PivotItem headerText="PnP Powershell" itemKey="PnPPowershell" />
                {/* <PivotItem headerText="PnP Template" itemKey="PnPTemplate" /> */}
                <PivotItem headerText="O365 CLI (Powershell)" itemKey="o365_PS" />
                <PivotItem headerText="O365 CLI (Bash)" itemKey="o365_Bash" />
            </Pivot>
            {currentExportPackage && <ExportPackageViewer exportPackage={currentExportPackage} />}
        </Panel>
    </div >;
}