preact#Component TypeScript Examples

The following examples show how to use preact#Component. 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: base.tsx    From gridjs with MIT License 6 votes vote down vote up
export abstract class BaseComponent<
  P extends BaseProps = unknown,
  S = unknown,
> extends Component<P, S> {
  protected config: Config;
  protected _: (message: string, ...args) => string;

  constructor(props: P, context: any) {
    super(props, context);
    this.config = getConfig(context);

    if (this.config) {
      this._ = useTranslator(this.config.translator);
    }
  }
}
Example #2
Source File: Transform.tsx    From LogicFlow with Apache License 2.0 6 votes vote down vote up
export default class TransfromGraph extends Component {
  render() {
    const { children } = this.props;
    return (
      <g>
        {children}
      </g>
    );
  }
}
Example #3
Source File: index.tsx    From big-web-quiz with Apache License 2.0 6 votes vote down vote up
export default class ColorSelect extends Component<Props, {}> {
  private _onClick = (event: Event) => {
    const onChange = this.props.onChange;
    if (!onChange) return;
    const el = event.currentTarget as HTMLInputElement;
    onChange(Number(el.value));
  };

  render({ name, disabled, value }: Props) {
    return (
      <div class={`color-select${disabled ? ' disabled' : ''}`}>
        {palette.map((paletteItem, i) => (
          <label>
            <input
              type="radio"
              name={name}
              value={i}
              disabled={disabled}
              checked={value === i}
              onClick={this._onClick}
            />
            <span
              class="color-select-item"
              style={{
                '--color-from': getHexColor(paletteItem[0]),
                '--color-to': getHexColor(paletteItem[1]),
              }}
            ></span>
          </label>
        ))}
      </div>
    );
  }
}
Example #4
Source File: BackgroundOverlay.tsx    From LogicFlow with Apache License 2.0 6 votes vote down vote up
export default class BackgroundOverlay extends Component<IProps> {
  render() {
    const { background } = this.props;
    return (
      <div className="lf-background">
        <div style={background} className="lf-background-area" />
      </div>
    );
  }
}
Example #5
Source File: ApplePayButton.tsx    From adyen-web with MIT License 6 votes vote down vote up
class ApplePayButton extends Component<ApplePayButtonProps> {
    public static defaultProps = {
        onClick: () => {},
        buttonColor: 'black',
        buttonType: 'plain'
    };

    render({ buttonColor, buttonType }) {
        /* eslint-disable jsx-a11y/no-static-element-interactions */
        return (
            <button
                type="button"
                aria-label={this.props.i18n.get('payButton')}
                lang={this.props.i18n.languageCode}
                className={cx(
                    'adyen-checkout__applepay__button',
                    `adyen-checkout__applepay__button--${buttonColor}`,
                    `adyen-checkout__applepay__button--${buttonType}`,
                    [styles['apple-pay']],
                    [styles['apple-pay-button']],
                    [styles[`apple-pay-button-${buttonColor}`]],
                    [styles[`apple-pay-button--type-${buttonType}`]]
                )}
                onClick={this.props.onClick}
            />
        );
        /* eslint-enable jsx-a11y/no-static-element-interactions */
    }
}
Example #6
Source File: audio.tsx    From big-web-quiz with Apache License 2.0 6 votes vote down vote up
export default class Audio extends Component<Props> {
  private _lastState: LoopStatus = 'stop';
  private _lastChildren?: preact.ComponentChildren;
  private _lockedChildren?: preact.ComponentChildren;
  private _audio = getAudio();

  async componentWillUnmount() {
    const audio = await this._audio;
    audio.stopLoops();
  }

  render({ state, children }: RenderableProps<Props>) {
    if (state !== this._lastState) {
      this._lastState = state;
      this._lockedChildren = this._lastChildren;
      this._audio.then(async audio => {
        if (state === 'play') {
          audio.playLoop();
        } else if (state === 'upgrade') {
          await audio.upgradeLoop();
        } else if (state === 'stop') {
          await audio.playStab();
        }

        this._lockedChildren = undefined;
        // Trigger a render
        this.setState({});
      });
    }

    this._lastChildren = children;
    return this._lockedChildren || children;
  }
}
Example #7
Source File: HtmlOverlay.tsx    From LogicFlow with Apache License 2.0 6 votes vote down vote up
// type InjectedProps = IProps & {
//   transformStyle: GraphTransform
// };

@observer
class HtmlOverlay extends Component<IProps> {
  // get InjectedProps() {
  //   return this.props as InjectedProps;
  // }
  render() {
    const {
      graphModel: {
        transformModel,
      },
    } = this.props;
    const { transform } = transformModel.getTransformStyle();
    const { children } = this.props;

    return (
      <div className="lf-html-overlay">
        <div className="lf-html-overlay__transform" style={transform}>
          {
            children
          }
        </div>
      </div>
    );
  }
}
Example #8
Source File: CoreProvider.tsx    From adyen-web with MIT License 6 votes vote down vote up
/**
 * CoreProvider Component
 * Wraps a component delaying the render until after the i18n module is fully loaded
 */
class CoreProvider extends Component<CoreProviderProps> {
    public state = {
        loaded: false
    };

    componentDidMount() {
        if (this.props.i18n) {
            this.props.i18n.loaded.then(() => {
                this.setState({ loaded: true });
            });
        } else {
            this.setState({ loaded: true });
        }
    }

    render({ children }: CoreProviderProps) {
        if (this.state.loaded) {
            return (
                <CoreContext.Provider
                    value={{ i18n: this.props.i18n, loadingContext: this.props.loadingContext, commonProps: this.props.commonProps || {} }}
                >
                    {toChildArray(children)}
                </CoreContext.Provider>
            );
        }

        return null;
    }
}
Example #9
Source File: ToolOverlay.tsx    From LogicFlow with Apache License 2.0 6 votes vote down vote up
@observer
export default class ToolOverlay extends Component<IProps> {
  setToolOverlayRef = (element) => {
    const { tool } = this.props;
    const lf: LogicFlow = tool.getInstance();
    lf.components.forEach(render => render(lf, element));
    lf.components = []; // 保证extension组件的render只执行一次
  };
  /**
   * 外部传入的一般是HTMLElement
   */
  getTools() {
    const { tool, graphModel } = this.props;
    const tools = tool.getTools();
    const components = tools.map(item => h(item, {
      graphModel,
      logicFlow: tool.instance,
    }));
    tool.components = components;
    return components;
  }
  render() {
    return (
      <div className="lf-tool-overlay" ref={this.setToolOverlayRef}>
        {
          this.getTools()
        }
      </div>
    );
  }
}
Example #10
Source File: ThreeDS2Form.tsx    From adyen-web with MIT License 6 votes vote down vote up
export default class ThreeDS2Form extends Component<ThreeDS2FormProps> {
    protected formEl;

    componentDidMount() {
        this.formEl.submit();
    }

    render({ name, action, target, inputName, inputValue }) {
        return (
            <form
                ref={ref => {
                    this.formEl = ref;
                }}
                method="POST"
                className={classNames(['adyen-checkout__threeds2__form', `adyen-checkout__threeds2__form--${name}`])}
                name={name}
                action={action}
                target={target}
                style={{ display: 'none' }}
            >
                <input name={inputName} value={inputValue} />
            </form>
        );
    }
}
Example #11
Source File: Graph.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
// todo: fixme type
// @ts-ignore
@observer
class Graph extends Component<IProps> {
  // get InjectedProps() {
  //   return this.props as InjectedProps;
  // }
  getComponent(model: BaseEdgeModel | BaseNodeModel, graphModel: GraphModel, overlay = 'canvas-overlay') {
    const { getView } = this.props;
    const View = getView(model.type);
    return (
      <View
        key={model.id}
        model={model}
        graphModel={graphModel}
        overlay={overlay}
      />
    );
  }
  render() {
    const {
      graphModel, tool, options, dnd, snaplineModel,
    } = this.props;
    const style: ContainerStyle = {};
    // 如果初始化的时候,不传宽高,则默认为父容器宽高。
    if (options.width) {
      style.width = `${graphModel.width}px`;
    }
    if (options.height) {
      style.height = `${graphModel.height}px`;
    }
    const { fakerNode, editConfigModel } = graphModel;
    const { adjustEdge } = editConfigModel;

    return (
      <div
        className="lf-graph"
        style={style}
      >
        <CanvasOverlay
          graphModel={graphModel}
          dnd={dnd}
        >
          <g className="lf-base">
            {
              map(graphModel.sortElements, (nodeModel) => (
                this.getComponent(nodeModel, graphModel)
              ))
            }
          </g>
          {
            fakerNode ? this.getComponent(fakerNode, graphModel) : ''
          }
        </CanvasOverlay>
        <ModificationOverlay graphModel={graphModel}>
          <OutlineOverlay graphModel={graphModel} />
          {adjustEdge ? <BezierAdjustOverlay graphModel={graphModel} /> : ''}
          {!options.isSilentMode && options.snapline !== false ? <SnaplineOverlay snaplineModel={snaplineModel} /> : ''}
        </ModificationOverlay>
        <ToolOverlay graphModel={graphModel} tool={tool} />
        {options.background && <BackgroundOverlay background={options.background} />}
        {options.grid && <Grid {...options.grid} graphModel={graphModel} />}
      </div>
    );
  }
}
Example #12
Source File: vote-buttons.tsx    From big-web-quiz with Apache License 2.0 5 votes vote down vote up
export default class VoteButtons extends Component<Props, State> {
  state: State = {
    currentVote: -1,
  };

  private _onButtonClick = (event: Event) => {
    const el = event.currentTarget as HTMLButtonElement;
    const index = Number(el.dataset.i);
    if (index === this.state.currentVote) return;
    this.setState({ currentVote: index });
    this.props.onVote(index);
  };

  render({ vote }: Props, { currentVote }: State) {
    return (
      <div class="vote-buttons">
        {vote.items.map((item, i) => [
          i > 0 ? <div class="vote-or">or</div> : '',
          <button
            class={`unbutton vote-button${
              currentVote === i
                ? ' vote-button-selected'
                : currentVote !== -1
                ? ' vote-button-unselected'
                : ''
            }`}
            style={{
              '--color-from': getHexColor(palette[item.colorId][0]),
              '--color-to': getHexColor(palette[item.colorId][1]),
            }}
            data-i={i}
            onClick={this._onButtonClick}
          >
            {item.label}
          </button>,
        ])}
      </div>
    );
  }
}
Example #13
Source File: header.ts    From gridjs with MIT License 5 votes vote down vote up
/**
   * Tries to automatically adjust the width of columns based on:
   *    - Header cell content
   *    - Cell content of the first row
   *    - Cell content of the last row
   *
   * @param config
   */
  adjustWidth(config: Config): this {
    const container: Element = config.container;
    const tableRef: RefObject<Component> = config.tableRef;
    const tempRef: RefObject<HTMLDivElement> = config.tempRef;
    const autoWidth = config.tempRef || true;

    if (!container) {
      // we can't calculate the width because the container
      // is unknown at this stage
      return this;
    }

    // pixels
    const containerWidth = container.clientWidth;

    // let's create a shadow table with the first 10 rows of the data
    // and let the browser to render the table with table-layout: auto
    // no padding, margin or border to get the minimum space required
    // to render columns. One the table is rendered and widths are known,
    // we unmount the shadow table from the DOM and set the correct width
    const shadowTable = createRef();
    let widths = {};

    if (tableRef.current && autoWidth) {
      // render a ShadowTable with the first 10 rows
      const el = h(ShadowTable, {
        tableRef: tableRef,
      });
      el.ref = shadowTable;

      render(el, tempRef.current);

      widths = shadowTable.current.widths();
    }

    for (const column of flatten(Header.tabularFormat(this.columns))) {
      // because we don't want to set the width of parent THs
      if (column.columns && column.columns.length > 0) {
        continue;
      }

      if (!column.width && autoWidth) {
        // tries to find the corresponding cell
        // from the ShadowTable and set the correct width

        if (column.id in widths) {
          // because a column can be hidden, too
          column.width = px(widths[column.id]['width']);
          column.minWidth = px(widths[column.id]['minWidth']);
        }
      } else {
        // column width is already defined
        // sets the column with based on the width of its container
        column.width = px(width(column.width, containerWidth));
      }
    }

    if (tableRef.current && autoWidth) {
      // unmount the shadow table from temp
      render(null, tempRef.current);
    }

    return this;
  }
Example #14
Source File: PaymentMethodList.tsx    From adyen-web with MIT License 5 votes vote down vote up
class PaymentMethodList extends Component<PaymentMethodListProps> {
    public static defaultProps: PaymentMethodListProps = {
        instantPaymentMethods: [],
        paymentMethods: [],
        activePaymentMethod: null,
        cachedPaymentMethods: {},
        orderStatus: null,
        onSelect: () => {},
        onDisableStoredPaymentMethod: () => {},
        isDisabling: false,
        isLoading: false
    };

    componentDidMount() {
        // Open first PaymentMethodItem
        if (this.props.paymentMethods[0]) {
            const firstPaymentMethod = this.props.paymentMethods[0];
            const shouldOpenFirstStored = this.props.openFirstStoredPaymentMethod && getProp(firstPaymentMethod, 'props.oneClick') === true;
            const shouldOpenFirstPaymentMethod = shouldOpenFirstStored || this.props.openFirstPaymentMethod;

            if (shouldOpenFirstPaymentMethod) {
                this.onSelect(firstPaymentMethod)();
            }
        }
    }

    public onSelect = paymentMethod => () => this.props.onSelect(paymentMethod);

    render({ paymentMethods, instantPaymentMethods, activePaymentMethod, cachedPaymentMethods, isLoading }) {
        const paymentMethodListClassnames = classNames({
            [styles['adyen-checkout__payment-methods-list']]: true,
            'adyen-checkout__payment-methods-list': true,
            'adyen-checkout__payment-methods-list--loading': isLoading
        });

        return (
            <Fragment>
                {this.props.orderStatus && (
                    <OrderPaymentMethods order={this.props.order} orderStatus={this.props.orderStatus} onOrderCancel={this.props.onOrderCancel} />
                )}

                {!!instantPaymentMethods.length && <InstantPaymentMethods paymentMethods={instantPaymentMethods} />}

                <ul className={paymentMethodListClassnames}>
                    {paymentMethods.map((paymentMethod, index, paymentMethodsCollection) => {
                        const isSelected = activePaymentMethod && activePaymentMethod._id === paymentMethod._id;
                        const isLoaded = paymentMethod._id in cachedPaymentMethods;
                        const isNextOneSelected =
                            activePaymentMethod &&
                            paymentMethodsCollection[index + 1] &&
                            activePaymentMethod._id === paymentMethodsCollection[index + 1]._id;

                        return (
                            <PaymentMethodItem
                                className={classNames({ 'adyen-checkout__payment-method--next-selected': isNextOneSelected })}
                                standalone={paymentMethods.length === 1}
                                paymentMethod={paymentMethod}
                                isSelected={isSelected}
                                isDisabling={isSelected && this.props.isDisabling}
                                isLoaded={isLoaded}
                                isLoading={isLoading}
                                onSelect={this.onSelect(paymentMethod)}
                                key={paymentMethod._id}
                                showRemovePaymentMethodButton={this.props.showRemovePaymentMethodButton}
                                onDisableStoredPaymentMethod={this.props.onDisableStoredPaymentMethod}
                            />
                        );
                    })}
                </ul>
            </Fragment>
        );
    }
}
Example #15
Source File: iframe.tsx    From big-web-quiz with Apache License 2.0 5 votes vote down vote up
export default class PresentationIframe extends Component<Props> {
  static getDerivedStateFromProps(props: Props, state: State): Partial<State> {
    const newState: Partial<State> = {};

    if (props.src !== state.src) {
      newState.src = props.src;
      newState.showIframe = false;
    }

    return newState;
  }

  state: State = {
    src: '',
    showIframe: false,
  };

  private _iframeLink?: IframeComlink;

  componentDidMount() {
    this.props.actionTarget.addEventListener('action', this._onAction);
  }

  componentWillUnmount() {
    this.props.actionTarget.removeEventListener('action', this._onAction);
  }

  private _onAction = (event: import('./').IframeActionEvent) => {
    if (!this._iframeLink) return;
    this._iframeLink[event.action]();
  };

  private _onIframeLoad = (event: Event) => {
    const el = event.currentTarget as HTMLIFrameElement;

    if (this._iframeLink) this._iframeLink[comlink.releaseProxy]();

    this._iframeLink = comlink.wrap(
      comlink.windowEndpoint(el.contentWindow!),
    ) as IframeComlink;

    this.setState({ showIframe: true });
  };

  private _onTransition = async (el: HTMLElement): Promise<void> => {
    const outgoing = el.children[0] as HTMLElement;

    if (outgoing.nodeName === 'IFRAME') {
      return animate(
        outgoing,
        { to: { opacity: '0' } },
        // easeInOutCubic
        { duration: 500, easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)' },
      );
    }
  };

  render(_: Props, { src, showIframe }: State) {
    return (
      <div class="presentation-iframe">
        <Transition onTransition={this._onTransition}>
          {src ? (
            <iframe
              key={src}
              class={showIframe ? 'show' : ''}
              src={src}
              onLoad={this._onIframeLoad}
            ></iframe>
          ) : (
            <div />
          )}
        </Transition>
      </div>
    );
  }
}
Example #16
Source File: BezierAdjustOverlay.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
// bezier曲线的可调整锚点
class BezierAdjustAnchor extends Component<IAnchorProps, IState> {
  dragHandler: StepDrag;
  constructor() {
    super();
    this.dragHandler = new StepDrag({
      onDraging: this.onDraging,
      onDragEnd: this.onDragEnd,
    });
  }
  onDraging = ({ event }) => {
    const { graphModel, bezierModel, type } = this.props;
    const {
      canvasOverlayPosition: { x, y },
    } = graphModel.getPointByClient({
      x: event.clientX,
      y: event.clientY,
    });
    bezierModel.updateAdjustAnchor({ x, y }, type);
  };
  onDragEnd = (() => {
    const { bezierModel } = this.props;
    bezierModel.isDragging = false;
  });
  render() {
    const { position } = this.props;
    const { x, y } = position;
    const { bezierModel } = this.props;
    const {
      adjustAnchor,
    } = bezierModel.getEdgeStyle();
    return (
      <Circle
        className="lf-bezier-adjust-anchor"
        x={x}
        y={y}
        {
          ...adjustAnchor
        }
        onMouseDown={(ev) => {
          // if (edgeAddable !== false) {
          this.dragHandler.handleMouseDown(ev);
          // }
        }}
      />
    );
  }
}
Example #17
Source File: Button.tsx    From adyen-web with MIT License 5 votes vote down vote up
class Button extends Component<ButtonProps, ButtonState> {
    public static defaultProps = {
        status: 'default',
        variant: 'primary',
        disabled: false,
        label: '',
        inline: false,
        target: '_self',
        onClick: () => {}
    };

    public onClick = e => {
        e.preventDefault();

        if (!this.props.disabled) {
            this.props.onClick(e, { complete: this.complete });
        }
    };

    public complete = (delay = 1000) => {
        this.setState({ completed: true });
        setTimeout(() => {
            this.setState({ completed: false });
        }, delay);
    };

    render({ classNameModifiers = [], disabled, href, icon, inline, label, status, variant }, { completed }) {
        const { i18n } = useCoreContext();

        const buttonIcon = icon ? <img className="adyen-checkout__button__icon" src={icon} alt="" aria-hidden="true" /> : '';

        const modifiers = [
            ...classNameModifiers,
            ...(variant !== 'primary' ? [variant] : []),
            ...(inline ? ['inline'] : []),
            ...(completed ? ['completed'] : []),
            ...(status === 'loading' || status === 'redirect' ? ['loading'] : [])
        ];

        const buttonClasses = classNames(['adyen-checkout__button', ...modifiers.map(m => `adyen-checkout__button--${m}`)]);

        const buttonStates = {
            loading: <Spinner size="medium" />,
            redirect: (
                <span className="adyen-checkout__button__content">
                    <Spinner size="medium" inline />
                    {i18n.get('payButton.redirecting')}
                </span>
            ),
            default: (
                <span className="adyen-checkout__button__content">
                    {buttonIcon}
                    <span className="adyen-checkout__button__text">{label}</span>
                </span>
            )
        };

        const buttonText = buttonStates[status] || buttonStates.default;

        if (href) {
            return (
                <a className={buttonClasses} href={href} disabled={disabled} target={this.props.target} rel={this.props.rel}>
                    {buttonText}
                </a>
            );
        }

        return (
            <button className={buttonClasses} type="button" disabled={disabled} onClick={this.onClick}>
                {buttonText}
            </button>
        );
    }
}
Example #18
Source File: BezierAdjustOverlay.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
@observer
export default class BezierAdjustOverlay extends Component<IProps> {
  getBezierAdjust(bezier: BezierEdgeModel, graphModel: GraphModel) {
    const { path, id } = bezier;
    const pointsList = getBezierPoints(path);
    const [start, sNext, ePre, end] = pointsList;
    const { adjustLine } = bezier.getEdgeStyle();
    const result = [];
    result.push(<Line
      x1={start.x}
      y1={start.y}
      x2={sNext.x}
      y2={sNext.y}
      {
        ...adjustLine
      }
    />);
    result.push(<BezierAdjustAnchor
      position={sNext}
      bezierModel={bezier}
      graphModel={graphModel}
      key={`${id}_ePre`}
      type="sNext"
    />);
    result.push(<Line
      x1={end.x}
      y1={end.y}
      x2={ePre.x}
      y2={ePre.y}
      {
        ...adjustLine
      }
    />);
    result.push(<BezierAdjustAnchor
      position={ePre}
      bezierModel={bezier}
      graphModel={graphModel}
      key={`${id}_sNext`}
      type="ePre"
    />);
    return result;
  }
  // 获取选中bezier曲线,调整操作线和锚点
  selectedBezierEdge() {
    const { graphModel } = this.props;
    const edgeList = graphModel.edges;
    const edgeAdjust = [];
    for (let i = 0; i < edgeList.length; i++) {
      const edge = edgeList[i];
      if (edge.isSelected && edge.modelType === ModelType.BEZIER_EDGE && edge.draggable) {
        edgeAdjust.push(this.getBezierAdjust(edge as BezierEdgeModel, graphModel));
      }
    }
    return edgeAdjust;
  }
  render() {
    return (
      <g className="lf-bezier-adjust">
        {this.selectedBezierEdge()}
      </g>
    );
  }
}
Example #19
Source File: GetDeviceFingerprint.tsx    From adyen-web with MIT License 5 votes vote down vote up
class GetDeviceFingerprint extends Component<GetDeviceFingerprintProps> {
    public postMessageDomain;
    public processMessageHandler;
    public deviceFingerPrintPromise;

    constructor(props) {
        super(props);

        this.postMessageDomain = getOrigin(this.props.loadingContext) || this.props.loadingContext;
    }

    getDfpPromise(): Promise<any> {
        return new Promise((resolve, reject) => {
            /**
             * Listen for postMessage responses from the notification url
             */
            this.processMessageHandler = getProcessMessageHandler(
                this.postMessageDomain,
                resolve,
                reject,
                FAILED_DFP_RESOLVE_OBJECT,
                DEVICE_FINGERPRINT
            );

            /* eslint-disable-next-line */
            window.addEventListener('message', this.processMessageHandler);
        });
    }

    componentDidMount() {
        // Get device fingerprint
        this.deviceFingerPrintPromise = promiseTimeout(DF_TIMEOUT, this.getDfpPromise(), FAILED_DFP_RESOLVE_OBJECT_TIMEOUT);
        this.deviceFingerPrintPromise.promise
            .then(resolveObject => {
                this.props.onCompleteFingerprint(resolveObject);
                window.removeEventListener('message', this.processMessageHandler);
            })
            .catch(rejectObject => {
                this.props.onErrorFingerprint(rejectObject);
                window.removeEventListener('message', this.processMessageHandler);
            });
    }

    render({ dfpURL }) {
        return (
            <div className="adyen-checkout-risk__device-fingerprint">
                <Iframe name={iframeName} src={dfpURL} allow={allowProperties} title="devicefingerprinting iframe" />
            </div>
        );
    }
}
Example #20
Source File: BaseText.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
export default class BaseText extends Component<IProps, IState> {
  dragHandler: (ev: MouseEvent) => void;
  sumDeltaX = 0;
  sumDeltaY = 0;
  stepDrag: StepDrag;
  constructor(config) {
    super();
    const { model, draggable } = config;
    this.stepDrag = new StepDrag({
      onDraging: this.onDraging,
      step: 1,
      model,
      isStopPropagation: draggable,
    });
  }
  getShape() {
    const { model, graphModel } = this.props;
    const { text } = model;
    const { editConfigModel } = graphModel;
    const { value, x, y, editable, draggable } = text;
    const attr = {
      x,
      y,
      className: '',
      value,
    };
    if (editable) {
      attr.className = 'lf-element-text';
    } else if (draggable || editConfigModel.nodeTextDraggable) {
      attr.className = 'lf-text-draggable';
    } else {
      attr.className = 'lf-text-disabled';
    }
    const style = model.getTextStyle();
    return (
      <Text {...attr} {...style} model={model} />
    );
  }
  onDraging = ({ deltaX, deltaY }) => {
    const {
      model,
      graphModel: {
        transformModel,
      },
    } = this.props;
    const [curDeltaX, curDeltaY] = transformModel.fixDeltaXY(deltaX, deltaY);
    model.moveText(curDeltaX, curDeltaY);
  };
  dblClickHandler = () => {
    // 静默模式下,双击不更改状态,不可编辑
    const { editable } = this.props;
    if (editable) {
      const { model } = this.props;
      model.setElementState(ElementState.TEXT_EDIT);
    }
  };
  mouseDownHandle = (ev: MouseEvent) => {
    const {
      draggable,
      graphModel: {
        editConfigModel: { nodeTextDraggable },
      },
    } = this.props;
    if (draggable || nodeTextDraggable) {
      this.stepDrag.handleMouseDown(ev);
    }
  };
  render() {
    const { model: { text } } = this.props;
    if (text) {
      return (
        <g onMouseDown={this.mouseDownHandle} onDblClick={this.dblClickHandler}>
          {
            this.getShape()
          }
        </g>
      );
    }
  }
}
Example #21
Source File: PrepareFingerprint3DS2.tsx    From adyen-web with MIT License 5 votes vote down vote up
class PrepareFingerprint3DS2 extends Component<PrepareFingerprint3DS2Props, PrepareFingerprint3DS2State> {
    public static type = 'scheme';

    constructor(props) {
        super(props);

        const { token, notificationURL } = this.props; // See comments on prepareFingerPrintData regarding notificationURL

        if (token) {
            const fingerPrintData = prepareFingerPrintData({ token, notificationURL });

            this.state = {
                status: 'init',
                fingerPrintData
            };
        } else {
            this.state = { status: 'error' };
            this.props.onError('Missing fingerprintToken parameter');
        }
    }

    public static defaultProps = {
        onComplete: () => {},
        onError: () => {},
        paymentData: '',
        showSpinner: true
    };

    componentDidMount() {
        // If no fingerPrintData or no threeDSMethodURL - don't render component. Instead exit with threeDSCompInd: 'U'
        if (!this.state.fingerPrintData || !this.state.fingerPrintData.threeDSMethodURL || !this.state.fingerPrintData.threeDSMethodURL.length) {
            this.setStatusComplete({ threeDSCompInd: 'U' });
            return;
        }

        // Render
        this.setState({ status: 'retrievingFingerPrint' });
    }

    setStatusComplete(resultObj: ResultObject) {
        this.setState({ status: 'complete' }, () => {
            /**
             * Create the data in the way that the endpoint expects:
             *  - this will be the /details endpoint for the 'old', v66, flow triggered by a 'threeDS2Fingerprint' action
             *  - and will be the /submitThreeDS2Fingerprint endpoint for the new, v67, 'threeDS2' action
             */
            const resolveDataFunction = this.props.useOriginalFlow ? createOldFingerprintResolveData : createFingerprintResolveData;
            const data = resolveDataFunction(this.props.dataKey, resultObj, this.props.paymentData);
            this.props.onComplete(data);
        });
    }

    render(props, { fingerPrintData }) {
        if (this.state.status === 'retrievingFingerPrint') {
            return (
                <DoFingerprint3DS2
                    onCompleteFingerprint={fingerprint => {
                        this.setStatusComplete(fingerprint.result);
                    }}
                    onErrorFingerprint={fingerprint => {
                        const errorObject = handleErrorCode(fingerprint.errorCode);
                        this.props.onError(errorObject);
                        this.setStatusComplete(fingerprint.result);
                    }}
                    showSpinner={this.props.showSpinner}
                    {...fingerPrintData}
                />
            );
        }

        return null;
    }
}
Example #22
Source File: index.tsx    From big-web-quiz with Apache License 2.0 5 votes vote down vote up
export default class AdminPresentationView extends Component<Props> {
  private _onRadioClick = (event: Event) => {
    const el = event.currentTarget as HTMLInputElement;
    const newVal = el.value as PresentationView;

    const patches: Operation[] = [
      {
        op: 'replace',
        path: `/presentationView`,
        value: newVal,
      },
    ];

    // Clear the zoom state of the bracket when we go back to the URL
    if (newVal === 'url') {
      patches.push({
        op: 'replace',
        path: `/bracketZoom`,
        value: null,
      });
    }

    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: patches,
    });
  };

  render({ mode }: Props) {
    return (
      <section>
        <h1>Presentation state</h1>
        <form class="radio-buttons">
          {Object.entries(presentationStates).map(([value, label]) => (
            <label>
              <input
                type="radio"
                name="mode"
                value={value}
                checked={value === mode}
                onClick={this._onRadioClick}
              />
              <span class="button">{label}</span>
            </label>
          ))}
        </form>
      </section>
    );
  }
}
Example #23
Source File: Grid.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
@observer
export default class Grid extends Component<IProps> {
  readonly id = createUuid();
  // 网格类型为点状
  renderDot() {
    const { config: { color, thickness = 2 }, size, visible } = this.props;
    const length = Math.min(Math.max(2, thickness), size / 2); // 2 < length < size /2
    let opacity = 1;
    if (!visible) {
      opacity = 0;
    }
    /* eslint-disable-next-line */
    return <rect width={length} height={length} rx={length / 2} ry={length / 2} fill={color} opacity={opacity} />;
  }
  // 网格类型为交叉线
  // todo: 采用背景缩放的方式,实现更好的体验
  renderMesh() {
    const { config: { color, thickness = 1 }, size, visible } = this.props;
    const strokeWidth = Math.min(Math.max(1, thickness), size / 2); // 1 < strokeWidth < size /2
    const d = `M ${size} 0 H0 M0 0 V0 ${size}`;
    let opacity = 1;
    if (!visible) {
      opacity = 0;
    }
    return <path d={d} stroke={color} strokeWidth={strokeWidth} opacity={opacity} />;
  }
  render() {
    // TODO 生成网格️️️✔、网格支持 options(size)✔
    const { type, size, graphModel: { transformModel } } = this.props;
    const {
      SCALE_X,
      SKEW_Y,
      SKEW_X,
      SCALE_Y,
      TRANSLATE_X,
      TRANSLATE_Y,
    } = transformModel;
    const matrixString = [SCALE_X, SKEW_Y, SKEW_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y].join(',');
    const transform = `matrix(${matrixString})`;
    // const transitionStyle = {
    //   transition: 'all 0.1s ease',
    // };
    return (
      <div className="lf-grid">
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%">
          <defs>
            <pattern
              id={this.id}
              patternUnits="userSpaceOnUse"
              patternTransform={transform}
              x="0"
              y="0"
              width={size}
              height={size}
            >
              {type === 'dot' && this.renderDot()}
              {type === 'mesh' && this.renderMesh()}
            </pattern>
          </defs>
          <rect width="100%" height="100%" fill={`url(#${this.id})`} />
        </svg>
      </div>
    );
  }
}
Example #24
Source File: index.tsx    From big-web-quiz with Apache License 2.0 4 votes vote down vote up
export default class BigScreen extends Component<Props, State> {
  private _ws = new WS('/big-screen/ws', msg => this._onWsMessage(msg));
  private _iframeActionTarget = new MessageChannel()
    .port1 as IframeActionTarget;

  state: State = {
    receivedData: false,
    iframe: '',
    topics: {},
    champions: {},
  };

  private _onWsMessage(message: string) {
    const data = JSON.parse(message);

    if ('iframeAction' in data) {
      this._iframeActionTarget.dispatchEvent(
        new IframeActionEvent({ action: data.iframeAction }),
      );
      return;
    }

    const interestingKeys: Array<keyof State> = [
      'results',
      'topics',
      'bracketZoom',
      'presentationView',
      'champions',
      'vote',
      'voteCount',
      'iframe',
    ];
    const receivedKeys = new Set(Object.keys(data.state));
    const hasInterestingKeys = interestingKeys.some(v => receivedKeys.has(v));

    if (!hasInterestingKeys) return;

    const newState: Partial<State> = {
      receivedData: true,
    };

    for (const key of interestingKeys) {
      if (key in data.state) {
        newState[key] = data.state[key];
      }
    }

    this.setState(newState);
  }

  componentWillUnmount() {
    this._ws.close();
  }

  private _onTransition = async (el: HTMLElement): Promise<void> => {
    const outgoing = el.children[0] as HTMLElement;
    const incoming = el.children[1] as HTMLElement;

    // Wait a microtask so the bracket can lay itself out before we apply transformations.
    await Promise.resolve();

    // Special 'slam' animation for the vote
    if (incoming.classList.contains('vote')) {
      animate(
        outgoing,
        { to: { opacity: '0', transform: 'translateZ(-600px)' } },
        {
          duration: 500,
          // ease-in cubic
          easing: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',
        },
      );

      return animate(
        incoming,
        { from: { opacity: '0', transform: 'translateZ(800px)' } },
        {
          duration: 200,
          delay: 300,
          // ease-in cubic
          easing: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',
          fill: 'backwards',
        },
      ).then(() => {
        const target = document.querySelector('.main-ui') as HTMLElement;
        const duration = 300;
        const maxDistance = 25;
        const start = performance.now();

        function frame() {
          const now = performance.now();
          const progress = Math.min((now - start) / duration, 1);
          const frameMaxDistance = maxDistance - maxDistance * progress;
          const x = Math.random() * frameMaxDistance * 2 - frameMaxDistance;
          const y = Math.random() * frameMaxDistance * 2 - frameMaxDistance;

          target.style.transform = `translate(${x}px, ${y}px)`;

          if (progress !== 1) {
            requestAnimationFrame(frame);
          }
        }

        requestAnimationFrame(frame);
      });
    }

    animate(
      outgoing,
      { to: { opacity: '0', transform: 'translateZ(-300px)' } },
      {
        duration: 500,
        // ease-in cubic
        easing: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',
      },
    ).then(() => {
      outgoing.style.opacity = '0';
    });

    return animate(
      incoming,
      { from: { opacity: '0', transform: 'translateZ(400px)' } },
      {
        duration: 500,
        delay: 400,
        // ease-out cubic
        easing: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
        fill: 'backwards',
      },
    ).then(() => {
      outgoing.style.opacity = '';
    });
  };

  render(
    _: Props,
    {
      results,
      topics,
      receivedData,
      bracketZoom,
      presentationView,
      champions,
      vote,
      voteCount,
      iframe,
    }: State,
  ) {
    if (!receivedData) return <div></div>;

    return (
      <div class="main-ui">
        <div
          class="site-title"
          style={{
            opacity:
              !vote && presentationView === 'bracket' && bracketZoom ? 0 : 1,
          }}
        >
          <h1>{title}</h1>
          <p>{subTitle}</p>
        </div>
        <Audio
          state={
            !vote || vote.state === 'results'
              ? 'stop'
              : vote.state === 'introducing'
              ? 'play'
              : vote.state === 'voting'
              ? 'upgrade'
              : 'stop'
          }
        >
          <Transition onTransition={this._onTransition}>
            {vote ? (
              <PresentationVote
                champions={champions}
                key={vote.id}
                vote={vote}
                voteCount={voteCount!}
              />
            ) : presentationView === 'url' ? (
              <div key="site-url" class="site-url">
                {location.host}
              </div>
            ) : presentationView === 'bracket' ? (
              results ? (
                <Bracket
                  key="bracket"
                  champions={champions}
                  results={results}
                  topics={topics}
                  toZoomTo={bracketZoom}
                />
              ) : (
                <div />
              )
            ) : (
              <PresentationChampionScores champions={champions} />
            )}
          </Transition>
        </Audio>
        <PresentationIframe
          src={iframe}
          actionTarget={this._iframeActionTarget}
        />
      </div>
    );
  }
}
Example #25
Source File: DropinComponent.tsx    From adyen-web with MIT License 4 votes vote down vote up
export class DropinComponent extends Component<DropinComponentProps, DropinComponentState> {
    public state: DropinComponentState = {
        elements: [],
        instantPaymentElements: [],
        orderStatus: null,
        isDisabling: false,
        status: { type: 'loading', props: undefined },
        activePaymentMethod: null,
        cachedPaymentMethods: {}
    };

    componentDidMount() {
        this.prepareDropinData();
    }

    public prepareDropinData = () => {
        const { order, clientKey, loadingContext } = this.props;
        const [storedElementsPromises, elementsPromises, instantPaymentsPromises] = this.props.onCreateElements();
        const orderStatusPromise = order ? getOrderStatus({ clientKey, loadingContext }, order) : null;

        Promise.all([storedElementsPromises, elementsPromises, instantPaymentsPromises, orderStatusPromise]).then(
            ([storedElements, elements, instantPaymentElements, orderStatus]) => {
                this.setState({ instantPaymentElements, elements: [...storedElements, ...elements], orderStatus });
                this.setStatus('ready');

                if (this.props.modules.analytics) {
                    this.props.modules.analytics.send({
                        containerWidth: this.base && (this.base as HTMLElement).offsetWidth,
                        paymentMethods: elements.map(e => e.props.type),
                        component: 'dropin',
                        flavor: 'dropin'
                    });
                }
            }
        );

        this.onOrderCancel = this.getOnOrderCancel();
    };

    public setStatus = (status: UIElementStatus, props: DropinStatusProps = {}) => {
        this.setState({ status: { type: status, props } });
    };

    private setActivePaymentMethod = paymentMethod => {
        this.setState(prevState => ({
            activePaymentMethod: paymentMethod,
            cachedPaymentMethods: { ...prevState.cachedPaymentMethods, [paymentMethod._id]: true }
        }));
    };

    componentDidUpdate(prevProps, prevState) {
        if (prevState.status.type !== this.state.status.type && this.state.activePaymentMethod) {
            this.state.activePaymentMethod.setStatus(this.state.status.type);
        }

        if (this.state.status.type === 'ready' && prevState.status.type !== 'ready' && this.props.onReady) {
            this.props.onReady();
        }
    }

    private handleOnSelectPaymentMethod = paymentMethod => {
        const { activePaymentMethod } = this.state;

        this.setActivePaymentMethod(paymentMethod);

        // onSelect event
        if ((activePaymentMethod && activePaymentMethod._id !== paymentMethod._id) || !activePaymentMethod) {
            this.props.onSelect(paymentMethod);
        }
    };

    private handleDisableStoredPaymentMethod = storedPaymentMethod => {
        this.setState({ isDisabling: true });

        new Promise((resolve, reject) => this.props.onDisableStoredPaymentMethod(storedPaymentMethod.props.storedPaymentMethodId, resolve, reject))
            .then(() => {
                this.setState(prevState => ({ elements: prevState.elements.filter(pm => pm._id !== storedPaymentMethod._id) }));
                this.setState({ isDisabling: false });
            })
            .catch(() => {
                this.setState({ isDisabling: false });
            });
    };

    closeActivePaymentMethod() {
        this.setState({ activePaymentMethod: null });
    }

    /**
     * getOnOrderCancel decides which onOrderCancel logic should be used, manual or sessions
     */
    private getOnOrderCancel = () => {
        if (this.props.onOrderCancel) {
            return (data: onOrderCancelData) => {
                this.props.onOrderCancel(data);
            };
        }
        if (this.props.session) {
            return (data: onOrderCancelData) =>
                this.props.session
                    .cancelOrder(data)
                    .then(() => this.props._parentInstance.update({ order: null }))
                    .catch(error => this.setStatus(error?.message || 'error'));
        }
        return null;
    };

    private onOrderCancel: (data: onOrderCancelData) => void;

    render(props, { elements, instantPaymentElements, status, activePaymentMethod, cachedPaymentMethods }) {
        const isLoading = status.type === 'loading';
        const isRedirecting = status.type === 'redirect';

        switch (status.type) {
            case 'success':
                return <Status.Success message={status.props?.message} />;

            case 'error':
                return <Status.Error message={status.props?.message} />;

            case 'custom':
                return status.props?.component?.render();

            default:
                return (
                    <div className={`adyen-checkout__dropin adyen-checkout__dropin--${status.type}`}>
                        {isRedirecting && status.props.component && status.props.component.render()}
                        {isLoading && status.props && status.props.component && status.props.component.render()}
                        {elements && !!elements.length && (
                            <PaymentMethodList
                                isLoading={isLoading || isRedirecting}
                                isDisabling={this.state.isDisabling}
                                paymentMethods={elements}
                                instantPaymentMethods={instantPaymentElements}
                                activePaymentMethod={activePaymentMethod}
                                cachedPaymentMethods={cachedPaymentMethods}
                                order={this.props.order}
                                orderStatus={this.state.orderStatus}
                                onOrderCancel={this.onOrderCancel}
                                onSelect={this.handleOnSelectPaymentMethod}
                                openFirstPaymentMethod={this.props.openFirstPaymentMethod}
                                openFirstStoredPaymentMethod={this.props.openFirstStoredPaymentMethod}
                                onDisableStoredPaymentMethod={this.handleDisableStoredPaymentMethod}
                                showRemovePaymentMethodButton={this.props.showRemovePaymentMethodButton}
                            />
                        )}
                    </div>
                );
        }
    }
}
Example #26
Source File: index.tsx    From big-web-quiz with Apache License 2.0 4 votes vote down vote up
export default class AdminBracket extends Component<Props> {
  private _onRegenerateClick = () => {
    if (!confirm('Are you sure? This will reset all votes.')) return;
    const promptAnswer = prompt(
      'How many in competition?',
      Object.keys(this.props.topics).length.toString(),
    );
    if (promptAnswer === null) return;

    fetchJSON('/admin/generate-bracket', {
      method: 'POST',
      body: { num: Number(promptAnswer) },
    });
  };

  private _onZoomOutClick = () => {
    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: [
        {
          op: 'replace',
          path: `/bracketZoom`,
          value: null,
        },
      ],
    });
  };

  private _onSelectBracketClick = (event: Event) => {
    const el = event.currentTarget as HTMLElement;
    const path = el.dataset.path!;
    this.props.onSelectBracket(path);
  };

  private _getResultLabel(result: Result | string): string {
    return getResultLabel(this.props.topics, result);
  }

  private _createBracketColumns(
    results: Result[],
    resultsPaths: string[],
    stage: number,
  ): JSX.Element[] {
    const toCreate = 2 ** stage / 2;
    const mapper = Array(toCreate).fill(undefined);

    const cols = [
      <div
        class={`bracket-column${stage === 1 ? ' bracket-column-semis' : ''}`}
      >
        {mapper.map((_, i) => (
          <div class="bracket-items-container">
            {results[i] && [
              ...results[i].items.map(item => (
                <div
                  class={`bracket-item${
                    typeof item === 'string' ? ' bracket-item-leaf' : ''
                  }`}
                >
                  {this._getResultLabel(item) ||
                    (typeof item === 'string' ? 'Unselected' : 'Undecided')}
                </div>
              )),
              <button
                class="button result-edit-button"
                data-path={resultsPaths[i]}
                onClick={this._onSelectBracketClick}
              >
                ...
              </button>,
            ]}
          </div>
        ))}
      </div>,
    ];

    const nextResults: Result[] = [];
    const nextResultsPaths: string[] = [];

    for (const [i, result] of results.entries()) {
      const path = resultsPaths[i];

      for (let itemI = 0; itemI < 2; itemI++) {
        if (typeof result.items[itemI] === 'string') continue;
        nextResults.push(result.items[itemI] as Result);
        nextResultsPaths.push(path + '/items/' + itemI);
      }
    }

    if (nextResults.length !== 0) {
      cols.push(
        ...this._createBracketColumns(nextResults, nextResultsPaths, stage + 1),
      );
    }

    return cols;
  }

  private _createBracket(result: Result): JSX.Element {
    const cols = [
      <div class="bracket-center">
        <div class="bracket-center-items">
          <div class="bracket-center-item">
            {this._getResultLabel(result.items[0]) ||
              (typeof result.items[0] === 'string'
                ? 'Unselected'
                : 'Undecided')}
          </div>
          <div class="bracket-center-vs">
            <button
              class="button"
              data-path="/"
              onClick={this._onSelectBracketClick}
            >
              ...
            </button>
          </div>
          <div class="bracket-center-item">
            {this._getResultLabel(result.items[1]) ||
              (typeof result.items[0] === 'string'
                ? 'Unselected'
                : 'Undecided')}
          </div>
        </div>
      </div>,
    ];

    for (let i = 0; i < 2; i++) {
      if (typeof result.items[i] === 'string') continue;
      const newCols = this._createBracketColumns(
        [result.items[i] as Result],
        ['/items/' + i],
        1,
      );

      if (i === 0) {
        cols.unshift(...newCols.reverse());
      } else {
        cols.push(...newCols);
      }
    }

    return <div class="bracket">{cols}</div>;
  }

  render({ results }: Props) {
    if (!results) {
      return (
        <AdminBracketWrapper
          onGenerateClick={this._onRegenerateClick}
          onZoomOut={this._onZoomOutClick}
        >
          <p>No bracket initialised. Press "Regenerate bracket".</p>
        </AdminBracketWrapper>
      );
    }

    return (
      <AdminBracketWrapper
        onGenerateClick={this._onRegenerateClick}
        onZoomOut={this._onZoomOutClick}
      >
        <div class="bracket-scroller">{this._createBracket(results)}</div>
      </AdminBracketWrapper>
    );
  }
}
Example #27
Source File: Anchor.tsx    From LogicFlow with Apache License 2.0 4 votes vote down vote up
class Anchor extends Component<IProps, IState> {
  preTargetNode: BaseNodeModel;
  sourceRuleResults: Map<TargetNodeId, ConnectRuleResult>; // 不同的target,source的校验规则产生的结果不同
  targetRuleResults: Map<TargetNodeId, ConnectRuleResult>; // 不同的target,target的校验规则不同
  dragHandler: StepDrag;
  t: any;
  constructor() {
    super();
    this.sourceRuleResults = new Map();
    this.targetRuleResults = new Map();

    this.state = {
      startX: 0,
      startY: 0,
      endX: 0,
      endY: 0,
      draging: false,
    };
    this.dragHandler = new StepDrag({
      onDragStart: this.onDragStart,
      onDraging: this.onDraging,
      onDragEnd: this.onDragEnd,
    });
  }
  getAnchorShape() {
    const {
      anchorData,
      style,
      node,
    } = this.props;
    const anchorShape = node.getAnchorShape(anchorData);
    if (anchorShape) return anchorShape;
    const { x, y } = anchorData;
    const hoverStyle = {
      ...style,
      ...style.hover,
    };
    return (
      <g>
        <Circle
          className="lf-node-anchor-hover"
          {...hoverStyle}
          {...{ x, y }}
        />
        <Circle
          className="lf-node-anchor"
          {...style}
          {...{ x, y }}
        />
      </g>
    );
  }
  onDragStart = ({ event }) => {
    const {
      anchorData, nodeModel, graphModel,
    } = this.props;
    const { overlapMode } = graphModel;
    // nodeModel.setSelected(true);
    graphModel.selectNodeById(nodeModel.id);
    if (overlapMode !== OverlapMode.INCREASE && nodeModel.autoToFront) {
      graphModel.toFront(nodeModel.id);
    }
    graphModel.eventCenter.emit(EventType.ANCHOR_DRAGSTART, {
      data: anchorData,
      e: event,
      nodeModel,
    });
    this.setState({
      startX: anchorData.x,
      startY: anchorData.y,
      endX: anchorData.x,
      endY: anchorData.y,
    });
  };
  onDraging = ({ event }) => {
    const {
      graphModel, nodeModel, anchorData,
    } = this.props;
    const {
      transformModel,
      eventCenter,
      width,
      height,
      editConfigModel,
    } = graphModel;
    const { clientX, clientY } = event;
    const {
      domOverlayPosition: { x, y },
      canvasOverlayPosition: { x: x1, y: y1 },
    } = graphModel.getPointByClient({
      x: clientX,
      y: clientY,
    });
    if (this.t) {
      clearInterval(this.t);
    }
    let nearBoundary = [];
    const size = 10;
    if (x < 10) {
      nearBoundary = [size, 0];
    } else if (x + 10 > width) {
      nearBoundary = [-size, 0];
    } else if (y < 10) {
      nearBoundary = [0, size];
    } else if (y + 10 > height) {
      nearBoundary = [0, -size];
    }
    this.setState({
      endX: x1,
      endY: y1,
      draging: true,
    });
    this.moveAnchorEnd(x1, y1);
    if (nearBoundary.length > 0 && !editConfigModel.stopMoveGraph) {
      this.t = setInterval(() => {
        const [translateX, translateY] = nearBoundary;
        transformModel.translate(translateX, translateY);
        const { endX, endY } = this.state;
        this.setState({
          endX: endX - translateX,
          endY: endY - translateY,
        });
        this.moveAnchorEnd(endX - translateX, endY - translateY);
      }, 50);
    }
    eventCenter.emit(EventType.ANCHOR_DRAG, {
      data: anchorData,
      e: event,
      nodeModel,
    });
  };
  onDragEnd = (event) => {
    if (this.t) {
      clearInterval(this.t);
    }
    this.checkEnd(event);
    this.setState({
      startX: 0,
      startY: 0,
      endX: 0,
      endY: 0,
      draging: false,
    });
    // 清除掉缓存结果 fix:#320 因为创建边之后,会影响校验结果变化,所以需要重新校验
    this.sourceRuleResults.clear();
    this.targetRuleResults.clear();
  };

  checkEnd = (event) => {
    const {
      graphModel, nodeModel, anchorData: { x, y, id },
    } = this.props;
    // nodeModel.setSelected(false);
    /* 创建边 */
    const { edgeType } = graphModel;
    const { endX, endY, draging } = this.state;
    const info = targetNodeInfo({ x: endX, y: endY }, graphModel);
    // 为了保证鼠标离开的时候,将上一个节点状态重置为正常状态。
    if (this.preTargetNode && this.preTargetNode.state !== ElementState.DEFAULT) {
      this.preTargetNode.setElementState(ElementState.DEFAULT);
    }
    // 没有draging就结束边
    if (!draging) return;
    if (info && info.node) {
      const targetNode = info.node;
      const anchorId = info.anchor.id;
      const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${id}`;
      const {
        isAllPass: isSourcePass,
        msg: sourceMsg,
      } = this.sourceRuleResults.get(targetInfoId) || {};
      const {
        isAllPass: isTargetPass,
        msg: targetMsg,
      } = this.targetRuleResults.get(targetInfoId) || {};
      if (isSourcePass && isTargetPass) {
        targetNode.setElementState(ElementState.DEFAULT);
        const edgeModel = graphModel.addEdge({
          type: edgeType,
          sourceNodeId: nodeModel.id,
          sourceAnchorId: id,
          startPoint: { x, y },
          targetNodeId: info.node.id,
          targetAnchorId: info.anchor.id,
          endPoint: { x: info.anchor.x, y: info.anchor.y },
        });
        const { anchorData } = this.props;
        graphModel.eventCenter.emit(EventType.ANCHOR_DROP, {
          data: anchorData,
          e: event,
          nodeModel,
          edgeModel,
        });
      } else {
        const nodeData = targetNode.getData();
        graphModel.eventCenter.emit(EventType.CONNECTION_NOT_ALLOWED, {
          data: nodeData,
          msg: targetMsg || sourceMsg,
        });
      }
    }
  };
  moveAnchorEnd(endX: number, endY: number) {
    const { graphModel, nodeModel, anchorData } = this.props;
    const info = targetNodeInfo({ x: endX, y: endY }, graphModel);
    if (info) {
      const targetNode = info.node;
      const anchorId = info.anchor.id;
      if (this.preTargetNode && this.preTargetNode !== info.node) {
        this.preTargetNode.setElementState(ElementState.DEFAULT);
      }
      // #500 不允许锚点自己连自己, 在锚点一开始连接的时候, 不触发自己连接自己的校验。
      if (anchorData.id === anchorId) {
        return;
      }
      this.preTargetNode = targetNode;
      // 支持节点的每个锚点单独设置是否可连接,因此规则key去nodeId + anchorId作为唯一值
      const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${anchorData.id}`;

      // 查看鼠标是否进入过target,若有检验结果,表示进入过, 就不重复计算了。
      if (!this.targetRuleResults.has(targetInfoId)) {
        const targetAnchor = info.anchor;
        const sourceRuleResult = nodeModel.isAllowConnectedAsSource(
          targetNode,
          anchorData,
          targetAnchor,
        );
        const targetRuleResult = targetNode.isAllowConnectedAsTarget(
          nodeModel,
          anchorData,
          targetAnchor,
        );
        this.sourceRuleResults.set(
          targetInfoId,
          formateAnchorConnectValidateData(sourceRuleResult),
        );
        this.targetRuleResults.set(
          targetInfoId,
          formateAnchorConnectValidateData(targetRuleResult),
        );
      }
      const { isAllPass: isSourcePass } = this.sourceRuleResults.get(targetInfoId);
      const { isAllPass: isTargetPass } = this.targetRuleResults.get(targetInfoId);
      // 实时提示出即将链接的锚点
      if (isSourcePass && isTargetPass) {
        targetNode.setElementState(ElementState.ALLOW_CONNECT);
      } else {
        targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT);
      }
    } else if (this.preTargetNode && this.preTargetNode.state !== ElementState.DEFAULT) {
      // 为了保证鼠标离开的时候,将上一个节点状态重置为正常状态。
      this.preTargetNode.setElementState(ElementState.DEFAULT);
    }
  }
  isShowLine() {
    const {
      startX,
      startY,
      endX,
      endY,
    } = this.state;
    const v = distance(startX, startY, endX, endY);
    return v > 10;
  }
  render() {
    const {
      startX,
      startY,
      endX,
      endY,
    } = this.state;
    const {
      anchorData: { edgeAddable }, edgeStyle,
    } = this.props;
    return (
      // className="lf-anchor" 作为下载时,需要将锚点删除的依据,不要修改类名
      <g className="lf-anchor">
        <g
          onMouseDown={(ev) => {
            if (edgeAddable !== false) {
              this.dragHandler.handleMouseDown(ev);
            }
          }}
        >
          { this.getAnchorShape() }
        </g>
        {this.isShowLine() && (
          <Line
            x1={startX}
            y1={startY}
            x2={endX}
            y2={endY}
            {...edgeStyle}
            pointer-events="none"
          />
        )}
      </g>
    );
  }
}
Example #28
Source File: index.tsx    From big-web-quiz with Apache License 2.0 4 votes vote down vote up
export default class AdminSelectedBracket extends Component<Props> {
  private _onTopicSelect = (event: Event) => {
    const el = event.currentTarget as HTMLSelectElement;
    const container = el.closest('.admin-form-item') as HTMLElement;

    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: [
        {
          op: 'replace',
          path: `/results${this.props.path}/items/${Number(
            container.dataset.itemIndex,
          )}`,
          value: el.value,
        },
      ],
    });
  };

  private _onChampionSelect = (event: Event) => {
    const el = event.currentTarget as HTMLSelectElement;
    const container = el.closest('.admin-form-item') as HTMLElement;
    const topicId = this.props.result!.items[
      Number(container.dataset.itemIndex)
    ];

    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: [
        {
          op: 'replace',
          path: `/topics/${escapePatchPathComponent(
            topicId as string,
          )}/championId`,
          value: el.value,
        },
      ],
    });
  };

  private _setWinner(index: number) {
    const path = this.props.path === '/' ? '' : this.props.path;

    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: [
        {
          op: 'replace',
          path: `/results${path}/winningIndex`,
          value: index,
        },
      ],
    });
  }

  private _onWinnerSelect = (event: Event) => {
    const el = event.currentTarget as HTMLSelectElement;
    const container = el.closest('.admin-form-item') as HTMLElement;
    this._setWinner(Number(container.dataset.itemIndex));
  };

  private _onShowSlides = (event: Event) => {
    const el = event.currentTarget as HTMLSelectElement;
    const container = el.closest('.admin-form-item') as HTMLElement;
    const index = Number(container.dataset.itemIndex);
    const topic = getResultTopic(
      this.props.topics,
      this.props.result!.items[index],
    );
    this.props.onShowSlides(topic!.slidesURL);
  };

  private _onClearWinner = () => {
    this._setWinner(-1);
  };

  private _onCreateVote = () => {
    this.props.onCreateVote();
  };

  private _onZoomHere = () => {
    let toHighlight: string[];

    if (this.props.path === '/') {
      toHighlight = ['0', '1'];
    } else {
      const parentPath = this.props.path
        .split('/items/')
        .slice(1)
        .join('-');

      toHighlight = [parentPath, parentPath + '-0', parentPath + '-1'];
    }

    fetchJSON('/admin/patch', {
      method: 'PATCH',
      body: [
        {
          op: 'replace',
          path: `/bracketZoom`,
          value: toHighlight,
        },
      ],
    });
  };

  render({ result, topics, champions }: Props) {
    if (!result) {
      return (
        <AdminSelectedBracketWrapper>
          <p>No bracket item selected</p>
        </AdminSelectedBracketWrapper>
      );
    }
    return (
      <AdminSelectedBracketWrapper>
        <div class="admin-form-items">
          {result.items.map((item, i) => {
            const isLeaf = typeof item === 'string';
            const topic = getResultTopic(topics, item);

            const topicAndChamp = isLeaf
              ? [
                  <div>
                    <label>
                      <span class="label">Topic</span>
                      <TopicsSelect
                        value={item as string}
                        topics={topics}
                        onInput={this._onTopicSelect}
                      />
                    </label>
                  </div>,
                  <div>
                    <label>
                      <span class="label">Champion</span>
                      <Select
                        disabled={!topic}
                        value={topic ? topic.championId : ''}
                        onInput={this._onChampionSelect}
                      >
                        <option value="">{topic ? 'None' : ''}</option>
                        {Object.entries(champions).map(([id, champion]) => (
                          <option value={id}>{champion.name}</option>
                        ))}
                      </Select>
                    </label>
                  </div>,
                ]
              : [
                  <div>
                    <span class="label">Topic</span>{' '}
                    <span class="prefilled-input">
                      {getResultLabel(topics, item) || 'Pending'}
                    </span>
                  </div>,
                  <div>
                    <span class="label">Champion</span>{' '}
                    <span class="prefilled-input">
                      {getResultChampionName(topics, item, champions) ||
                        'Pending'}
                    </span>
                  </div>,
                ];

            return (
              <div class="admin-form-item" data-item-index={i}>
                {topicAndChamp}
                <div>
                  <button
                    class="button"
                    disabled={result.winningIndex === i}
                    onClick={this._onWinnerSelect}
                  >
                    Set as winner
                  </button>{' '}
                  {topic && topic.slidesURL && (
                    <button class="button" onClick={this._onShowSlides}>
                      Show slides
                    </button>
                  )}
                </div>
              </div>
            );
          })}
          <div class="admin-form-extras">
            <div class="label">Actions</div>
            <div>
              <button
                class="button"
                disabled={result.winningIndex === -1}
                onClick={this._onClearWinner}
              >
                Clear winner
              </button>{' '}
              <button class="button" onClick={this._onCreateVote}>
                Create vote
              </button>{' '}
              <button class="button" onClick={this._onZoomHere}>
                Zoom here
              </button>
            </div>
          </div>
        </div>
      </AdminSelectedBracketWrapper>
    );
  }
}
Example #29
Source File: MultipleSelectTool.tsx    From LogicFlow with Apache License 2.0 4 votes vote down vote up
@observer
export default class MultipleSelect extends Component<IProps> {
  static toolName = 'multipleSelect';
  stepDrag;
  constructor(props) {
    super();
    const {
      graphModel: { gridSize, eventCenter },
    } = props;
    this.stepDrag = new StepDrag({
      onDraging: this.onDraging,
      step: gridSize,
      eventType: 'SELECTION',
      eventCenter,
    });
  }
  handleMouseDown = (ev: MouseEvent) => {
    this.stepDrag.handleMouseDown(ev);
  };
  onDraging = ({ deltaX, deltaY }) => {
    const { graphModel } = this.props;
    const selectElements = graphModel.getSelectElements(true);
    graphModel.moveNodes(selectElements.nodes.map(node => node.id), deltaX, deltaY);
  };
  handleContextMenu = (ev: MouseEvent) => {
    ev.preventDefault();
    const { graphModel, graphModel: { eventCenter, selectElements } } = this.props;
    const position = graphModel.getPointByClient({
      x: ev.clientX,
      y: ev.clientY,
    });
    const selectGraphData = {
      nodes: [],
      edges: [],
    };
    const models = [...selectElements.values()];
    models.forEach((model) => {
      if (model.BaseType === ElementType.NODE) {
        selectGraphData.nodes.push(model.getData());
      }
      if (model.BaseType === ElementType.EDGE) {
        selectGraphData.edges.push(model.getData());
      }
    });
    eventCenter.emit(EventType.SELECTION_CONTEXTMENU, {
      data: selectGraphData,
      e: ev,
      position,
    });
  };
  render() {
    const { graphModel: { selectElements, transformModel } } = this.props;
    if (selectElements.size <= 1) return;
    let x = Number.MAX_SAFE_INTEGER;
    let y = Number.MAX_SAFE_INTEGER;
    let x1 = Number.MIN_SAFE_INTEGER;
    let y1 = Number.MIN_SAFE_INTEGER;
    selectElements.forEach(element => {
      let outline = {
        x: 0,
        y: 0,
        x1: 0,
        y1: 0,
      };
      if (element.BaseType === ElementType.NODE) outline = getNodeOutline(element);
      if (element.BaseType === ElementType.EDGE) outline = getEdgeOutline(element);
      x = Math.min(x, outline.x);
      y = Math.min(y, outline.y);
      x1 = Math.max(x1, outline.x1);
      y1 = Math.max(y1, outline.y1);
    });
    [x, y] = transformModel.CanvasPointToHtmlPoint([x, y]);
    [x1, y1] = transformModel.CanvasPointToHtmlPoint([x1, y1]);
    const style = {
      left: `${x - 10}px`,
      top: `${y - 10}px`,
      width: `${x1 - x + 20}px`,
      height: `${y1 - y + 20}px`,
    };
    return (
      <div
        className="lf-multiple-select"
        style={style}
        onMouseDown={this.handleMouseDown}
        onContextMenu={this.handleContextMenu}
      />
    );
  }
}