office-ui-fabric-react#TextField TypeScript Examples

The following examples show how to use office-ui-fabric-react#TextField. 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: SpfxFluentuiDropdown.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
public render(): React.ReactElement<ISpfxFluentuiDropdownProps> {
    return (
      <div className={styles.spfxFluentuiDropdown}>
        <TextField
          className={styles.fixedwidth}
          label="Title" value={this.state.salestitle} onChanged={(titlevalue) => this.setState({ salestitle: titlevalue })} />
        {this.state.seletedprojects == null ? '' : <Dropdown
          placeholder="Select projects"
          label="Projects"
          onChange={this.projects_selection}
          multiSelect
          options={this.state.projectlookupvalues}
          className={styles.fixedwidth}
          defaultSelectedKeys={this.state.seletedprojects}
        />}

        <br />
        <PrimaryButton text="Save" onClick={this._savesales} />
      </div>
    );
  }
Example #2
Source File: SpfxPnpPageprovisioning.tsx    From SPFx with Mozilla Public License 2.0 6 votes vote down vote up
public render(): React.ReactElement<ISpfxPnpPageprovisioningProps> {
    return (
      <div className={styles.spfxPnpPageprovisioning}>
        <TextField label="Page Name" onChanged={(val) => { this.setState({ name: val }) }} />
        <TextField label="Page Title" onChanged={(val) => { this.setState({ title: val }) }} />
        <br />
        <PrimaryButton text="Create Page" onClick={this._CreatePage} />
      </div>
    );
  }
Example #3
Source File: Adder.tsx    From sp-site-designs-studio with MIT License 5 votes vote down vote up
Adder = (props: IAdderProps) => {

    const [isSelecting, setIsSelecting] = useState<boolean>(false);
    const [searchCriteria, setSearchCriteria] = useState<string>('');
    const addButtonRef = useRef<HTMLDivElement>();
    const searchBoxRef = useRef<ITextField>();

    useEffect(() => {
        if (isSelecting && searchBoxRef && searchBoxRef.current) {
            searchBoxRef.current.focus();
        }
    });

    const onSelected = (item: IAddableItem) => {
        props.onSelectedItem(item);
        setIsSelecting(false);
        setSearchCriteria('');
    };

    const onSearchCriteriaChanged = (ev, criteria) => {
        setSearchCriteria(criteria);
    };


    return <div className={styles.Adder}>
        <button className={`${styles.button} ${isSelecting ? styles.isSelecting : ""}`} onClick={() => setIsSelecting(true)}>
            <div ref={addButtonRef} className={styles.plusIcon}>
                <Icon iconName="Add" />
            </div>
        </button>
        {isSelecting && <Callout
            role="alertdialog"
            gapSpace={0}
            target={addButtonRef.current}
            onDismiss={() => setIsSelecting(false)}
            setInitialFocus={true}
            directionalHint={DirectionalHint.bottomCenter}
        >
            <div className={styles.row}>
                <div className={styles.fullWidth}>
                    <Stack horizontal>
                        <div className={styles.iconSearch}>
                            <Icon iconName="Search" />
                        </div>
                        <TextField
                            key="ItemSearchBox"
                            borderless
                            componentRef={searchBoxRef}
                            placeholder={props.searchBoxPlaceholderText || "Search an item..."} onChange={onSearchCriteriaChanged} />
                    </Stack>
                </div>
            </div>
            {renderItemsList(props.items, searchCriteria && searchCriteria.toLowerCase(), onSelected, props.noAvailableItemsText)}
        </Callout>}
    </div>;
}
Example #4
Source File: SitePicker.tsx    From sp-site-designs-studio with MIT License 5 votes vote down vote up
SitePicker = (props: ISitePickerProps) => {

    const sitesService = props.serviceScope.consume(SitesServiceKey);
    const [selectedSite, setSelectedSite] = useState<ISPSite>(null);
    const [isSearching, setIsSearching] = useState<boolean>(true);
    const onFilterChanged = async (filterText: string) => {
        return filterText
            ? (await sitesService.getSiteByNameOrUrl(filterText)).map(s => ({ key: s.url, name: s.title } as ITag))
            : [];
    };

    const onSelectedSiteChanged = (site: ISPSite) => {
        setSelectedSite(site);
        setIsSearching(false);
        props.onSiteSelected((site && site.url) || "");
    };

    const onSelectedTagChanged = (tag: ITag) => {
        onSelectedSiteChanged({ title: tag.name, url: tag.key, id: null });
        return tag;
    };

    // return <div className={styles.SitePicker}>
    return <div className={""/* styles.SitePicker */}>

        {!isSearching
            ? <Stack horizontal tokens={{ childrenGap: 5 }}>
                <TextField label={props.label} value={selectedSite && selectedSite.url} onChange={(_, v) => onSelectedSiteChanged({ url: v, title: '', id: '' })} />
                <IconButton styles={{ root: { position: "relative", top: 30 } }} iconProps={{ iconName: "SearchAndApps" }} onClick={() => setIsSearching(true)} />
            </Stack>
            : <div>{props.label && <Label>{props.label}</Label>}
                <Stack horizontal tokens={{ childrenGap: 5 }}>
                    <TagPicker
                        removeButtonAriaLabel="Remove"
                        onRenderSuggestionsItem={SuggestedSiteItem as any}
                        onResolveSuggestions={onFilterChanged}
                        pickerSuggestionsProps={{
                            suggestionsHeaderText: 'Suggested sites',
                            noResultsFoundText: 'No sites Found',
                        }}
                        resolveDelay={800}
                        itemLimit={1}
                        onItemSelected={onSelectedTagChanged}
                    />
                <IconButton styles={{ root: { position: "relative" } }} iconProps={{ iconName: "Edit" }} onClick={() => setIsSearching(false)} />
                </Stack>
            </div>}
    </div>;
}
Example #5
Source File: SpfxFluentuiPanel.tsx    From SPFx with Mozilla Public License 2.0 5 votes vote down vote up
public render(): React.ReactElement<ISpfxFluentuiPanelProps> {
    let buttonStyles = { root: { marginRight: 8 } };
    const onRenderFooterContent = () => (
      <div>
        <PrimaryButton onClick={this._saveclick} styles={buttonStyles}>
          Save
        </PrimaryButton>
        <DefaultButton onClick={this._cancelclick}>Cancel</DefaultButton>
      </div>
    );

    return (
      <div className={styles.spfxFluentuiPanel}>
        <Stack tokens={stackTokens} verticalAlign="end">
          <Stack horizontal tokens={stackTokens} verticalAlign="end">
            <Dropdown className={styles.Dropdown}
              placeholder="Select a Project"
              label="Projects"
              options={this.state.projects}
            />
            <DefaultButton text="Project" iconProps={addIcon} onClick={() => this.setState({ showpanel: true, projectname: '' })} />
          </Stack>
        </Stack>
        {this.state.showpanel &&
          <Panel
            headerText={"New Project Name"}
            isOpen={true}
            isBlocking={false}
            closeButtonAriaLabel="Close"
            onRenderFooterContent={onRenderFooterContent}>
            <TextField placeholder={'Enter a new project name'} onChanged={(strproject) => this.setState({ projectname: strproject })}></TextField>
          </Panel>
        }

      </div>
    );
  }
Example #6
Source File: SpfxReactHooks.tsx    From SPFx with Mozilla Public License 2.0 5 votes vote down vote up
function simplehooks(props: ISpfxReactHooksProps) {
  const [fruits, setfruits] = useState([])
  const [firstName, setFistName] = useState("No first Name")
  const [lastName, setLastName] = useState("No last Name")

  useEffect(() => {
    sp.setup({
      spfxContext: props.context
    });
    _getListItemsFromSP()
  }, []);

  const _getListItemsFromSP = async () => {
    const allItems: any[] = await sp.web.lists.getByTitle("Fruits").items.getAll();
    let titlevalues: string[] = [];
    allItems.forEach(function (v, i) {
      titlevalues.push(v.Title);
    })
    setfruits(titlevalues);
  }

  const _onbtnclick = () => {
    console.log('Changing value')
    setFistName('Ravichandran')
  }

  const _lastNameChanged = (changedvalue: any) => {
    setLastName(changedvalue)
  }


  return (<div>
    <b>Props value</b><br />
    {props.description}
    <br />
    <hr />
    <br />
    <b>State values</b><br />
    Name : {firstName + ' ' + lastName}
    <br />
    <br />
    <TextField label="Last Name" onChanged={_lastNameChanged} value={lastName} />
    <br />
    <br />
    <PrimaryButton text="Change First Name" onClick={() => _onbtnclick()} />
    <br />
    <hr />
    <br />
    <b>Loop async values from SharePoint List</b><br />
    {fruits.map(function (fruit, i) {
      return <h3 key={i}>{fruit}</h3>
    })}
  </div>);
}
Example #7
Source File: MeetingPage.tsx    From msteams-meetings-template with MIT License 4 votes vote down vote up
function MeetingPageComponent(props: MeetingPageProps) {
  const [validationEnabled, setValidationEnabled] = useState(false);

  function onSubjectChanged(
    evt: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
    newValue: string | undefined
  ) {
    // The meeting objects are small, cloning is cheap enough
    // Normally would use immutable records or similar to avoid overhead.
    const nextMeeting = _.cloneDeep(props.meeting);
    nextMeeting.subject = newValue ?? '';
    props.setMeeting(nextMeeting);
  }

  function onStartDateSelected(date?: Moment) {
    const nextMeeting = _.cloneDeep(props.meeting);
    nextMeeting.startDateTime = date ?? nextMeeting.startDateTime;

    // If start >= end, adjust to be the same delta as before from the start time
    if (nextMeeting.startDateTime.isSameOrAfter(nextMeeting.endDateTime)) {
      const existingDelta = moment(props.meeting.endDateTime).diff(
        moment(props.meeting.startDateTime)
      );
      const newEndDateTime = moment(nextMeeting.startDateTime).add(
        existingDelta
      );
      if (nextMeeting.startDateTime.isSameOrAfter(newEndDateTime)) {
        newEndDateTime.add(existingDelta);
      }
      nextMeeting.endDateTime = newEndDateTime;
    }

    props.setMeeting(nextMeeting);
  }

  function onEndDateSelected(date?: Moment) {
    const nextMeeting = _.cloneDeep(props.meeting);
    const newEndDateTime = date ?? nextMeeting.endDateTime;

    // Allow the change only if it maintains start < end
    if (!nextMeeting.startDateTime.isAfter(newEndDateTime)) {
      nextMeeting.endDateTime = newEndDateTime;
    }

    props.setMeeting(nextMeeting);
  }

  function onCreate() {
    if (!!props.validationFailures.invalidTitle) {
      setValidationEnabled(true);
      return;
    }

    props.createMeeting(props.meeting);
  }

  if (props.creationInProgress) {
    return (
      <div className="spinnerContainer">
        <Spinner size={SpinnerSize.large} />
      </div>
    );
  }

  return (
    <div className="newMeetingContainer">
      <Stack
        className="container"
        verticalFill
        tokens={{
          childrenGap: 35
        }}
      >
        <Stack horizontal tokens={{ childrenGap: 15 }}>
          <StackItem grow>
            <FontIcon iconName="Calendar" className={meetingIconClass} />
            <Text variant="xLarge" styles={boldStyle}>
              <FormattedMessage id="meetingPage.header" />
            </Text>
          </StackItem>
          <StackItem align="end" className="newMeetingButtons">
            <Stack horizontal tokens={{ childrenGap: 10 }}>
              <PrimaryButton
                className="teamsButton"
                disabled={props.creationInProgress}
                onClick={() => onCreate()}
                ariaLabel={translate('meetingPage.create.ariaLabel')}
              >
                <FormattedMessage id="meetingPage.create" />
              </PrimaryButton>
              <DefaultButton
                className="teamsButtonInverted"
                disabled={props.creationInProgress}
                onClick={() => props.cancel()}
                ariaLabel={translate('meetingPage.cancel.ariaLabel')}
              >
                <FormattedMessage id="meetingPage.cancel" />
              </DefaultButton>
            </Stack>
          </StackItem>
        </Stack>
        <Stack horizontal>
          <StackItem className="newMeetingInputIcon">
            <FontIcon iconName="Edit" className={inputIconClass} />
          </StackItem>
          <StackItem grow>
            <TextField
              className="newMeetingInput"
              placeholder={translate('meetingPage.title.input')}
              value={props.meeting?.subject}
              underlined
              onChange={onSubjectChanged}
              errorMessage={
                validationEnabled
                  ? props.validationFailures.invalidTitle
                  : undefined
              }
            />
          </StackItem>
        </Stack>

        <div className="newMeetingDatePickerContainer">
          <FontIcon iconName="Clock" className={inputIconClass} />
          <div className="newMeetingPicker">
            <DateTimePicker
              dateTime={props.meeting.startDateTime}
              minDate={moment()}
              onTimeUpdated={onStartDateSelected}
              includeDuration={false}
              iconName="ReplyAlt"
            />
            <DateTimePicker
              dateTime={props.meeting.endDateTime}
              minDate={props.meeting.startDateTime}
              onTimeUpdated={onEndDateSelected}
              includeDuration={true}
            />
          </div>
        </div>

        {/* MOBILE BUTTON GROUP */}
      </Stack>
      <StackItem className="newMeetingButtonsMobile">
        <Stack horizontal tokens={{ childrenGap: 10 }}>
          <PrimaryButton
            className="teamsButton teamsButtonFullWidth"
            disabled={props.creationInProgress}
            onClick={() => onCreate()}
            ariaLabel={translate('meetingPage.create.ariaLabel')}
          >
            <FormattedMessage id="meetingPage.create" />
          </PrimaryButton>
          <DefaultButton
            className="teamsButtonInverted teamsButtonFullWidth"
            disabled={props.creationInProgress}
            onClick={() => props.cancel()}
            ariaLabel={translate('meetingPage.cancel.ariaLabel')}
          >
            <FormattedMessage id="meetingPage.cancel" />
          </DefaultButton>
        </Stack>
      </StackItem>
    </div>
  );
}
Example #8
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 #9
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 #10
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 >;
}