{"version":3,"file":"model-editor.cjs","sources":["../src/model-editor.ts"],"sourcesContent":["// @ts-nocheck\nimport { Component, createContext, ReactElement, useContext } from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport { DateInput } from \"@blueprintjs/datetime\";\nimport { EditableText } from \"@blueprintjs/core\";\nimport { EditButton, DeleteButton } from \"./buttons\";\nimport { StatefulComponent } from \"./util\";\nimport classNames from \"classnames\";\nimport update, { Spec } from \"immutability-helper\";\n\nimport \"@blueprintjs/datetime/lib/css/blueprint-datetime.css\";\n\nconst ModelEditorContext = createContext<any>({});\n\nfunction ModelEditButton(props): ReactElement {\n  const { isEditing, actions } = useContext(ModelEditorContext);\n  return h(EditButton, {\n    isEditing,\n    onClick: actions.toggleEditing,\n    ...props,\n  });\n}\n\ninterface ModelEditorProps<T> {\n  model: T;\n  canEdit: boolean;\n  isEditing: boolean;\n  persistChanges(v: T, spec: Spec<T>): void;\n}\n\ninterface ModelEditorState<T> {\n  isEditing: boolean;\n  isPersisting: boolean | null;\n  error: any | null;\n  model: T;\n  initialModel: T;\n}\n\nclass ModelEditor<T> extends StatefulComponent<\n  ModelEditorProps<T>,\n  ModelEditorState<T>\n> {\n  constructor({ canEdit = true, ...props }) {\n    super(...arguments);\n    this.getValue = this.getValue.bind(this);\n    this.hasChanges = this.hasChanges.bind(this);\n    this.onChange = this.onChange.bind(this);\n    this.toggleEditing = this.toggleEditing.bind(this);\n    this.persistChanges = this.persistChanges.bind(this);\n    this.onPersistChanges = this.onPersistChanges.bind(this);\n    this.state = {\n      isEditing: props.isEditing || false,\n      isPersisting: null,\n      error: null,\n      model: props.model,\n      initialModel: props.model,\n    };\n  }\n\n  render(): ReactElement {\n    const { model } = this.state;\n    const { canEdit } = this.props;\n    const isEditing = this.state.isEditing && canEdit;\n    const actions = (() => {\n      let onChange, persistChanges, toggleEditing, updateState;\n      return ({ onChange, toggleEditing, updateState, persistChanges } = this);\n    })();\n    const value = {\n      actions,\n      model,\n      isEditing,\n      canEdit,\n      hasChanges: this.hasChanges,\n    };\n    return h(ModelEditorContext.Provider, { value }, this.props.children);\n  }\n\n  getValue(field) {\n    return this.state.model[field];\n  }\n\n  hasChanges(field) {\n    if (field == null) {\n      return this.state.initialModel !== this.state.model;\n    }\n    return this.state.initialModel[field] !== this.state.model[field];\n  }\n\n  onChange(field) {\n    return (value) => {\n      // @ts-ignore\n      return this.updateState({ model: { [field]: { $set: value } } });\n    };\n  }\n\n  toggleEditing() {\n    const spec: any = { $toggle: [\"isEditing\"] };\n    if (this.state.isEditing) {\n      spec.model = { $set: this.state.initialModel };\n    }\n    return this.updateState(spec);\n  }\n\n  onPersistChanges() {\n    return this.persistChanges(null);\n  }\n\n  async persistChanges(spec: any | undefined) {\n    const { persistChanges } = this.props;\n    // Persist changes expects a promise\n\n    let updatedModel = this.state.model;\n    if (spec != null) {\n      // If changeset is provided, we need to integrate\n      // it before proceeding\n      console.log(spec);\n      updatedModel = update(this.state.model, spec);\n    }\n    console.log(updatedModel);\n\n    let ret = null;\n    if (persistChanges == null) {\n      return null;\n    }\n    try {\n      this.updateState({ isPersisting: { $set: true } });\n\n      // Compute a shallow changeset of the model fields\n      const changeset: any = {};\n      for (let k in updatedModel) {\n        const v = updatedModel[k];\n        if (v === this.state.initialModel[k]) {\n          continue;\n        }\n        changeset[k] = v;\n      }\n\n      ret = await persistChanges(updatedModel, changeset);\n    } catch (err) {\n      console.error(err);\n    } finally {\n      spec = { isPersisting: { $set: false } };\n\n      if (ret != null) {\n        // @ts-ignore\n        const newModel = update(this.state.initialModel, { $merge: ret });\n        console.log(newModel);\n        spec.model = { $set: newModel };\n        spec.initialModel = { $set: newModel };\n      }\n      this.updateState(spec);\n    }\n  }\n\n  componentDidUpdate(prevProps) {\n    if (prevProps == null) {\n      return;\n    }\n    if (prevProps === this.props) {\n      return;\n    }\n    const spec: any = {};\n    if (\n      this.props.isEditing !== prevProps.isEditing &&\n      this.props.isEditing !== this.state.isEditing\n    ) {\n      spec.isEditing = { $set: this.props.isEditing };\n    }\n    if (this.props.model !== prevProps.model) {\n      spec.initialModel = { $set: this.props.model };\n      if (!this.props.isEditing) {\n        // If we aren't in edit mode, we want to propagate changes into the model\n        spec.model = { $set: this.props.model };\n      }\n    }\n    return this.updateState(spec);\n  }\n}\n\nfunction EditableMultilineText(props: any): React.ReactNode {\n  let { field, className } = props;\n  const { actions, model, isEditing } = useContext(ModelEditorContext);\n  let value = model[field];\n  const onChange = actions.onChange(field);\n  className = classNames(className, `field-${field}`, {\n    edited: useFieldHasChanges(field),\n  });\n\n  if (isEditing) {\n    value = h(EditableText, {\n      placeholder: `Edit ${field}`,\n      multiline: true,\n      className,\n      onChange,\n      value,\n    });\n  }\n  return h(\"div.text\", { className }, value);\n}\n\nconst useFieldHasChanges = (field) => {\n  const { hasChanges } = useContext(ModelEditorContext);\n  return hasChanges(field);\n};\n\nclass EditableDateField extends Component<any, any> {\n  static contextType = ModelEditorContext;\n  render() {\n    const { field } = this.props;\n    const { actions, model, isEditing } = this.context;\n    const value = model[field];\n\n    const className = classNames(\"date-input\", {\n      edited: this.context.hasChanges(field),\n      disabled: !isEditing,\n    });\n\n    let valueText = value;\n    if (value instanceof Date) {\n      valueText = value.toLocaleDateString();\n    }\n\n    if (!isEditing) {\n      return h(\"div\", { className }, valueText);\n    }\n    return h(DateInput, {\n      className,\n      value: new Date(value),\n      formatDate: (date) => date.toLocaleDateString(),\n      placeholder: valueText ?? \"Select date...\",\n      showActionsBar: true,\n      onChange: actions.onChange(field),\n      parseDate(d) {\n        return new Date(d);\n      },\n    });\n  }\n}\n\nconst useModelEditor = () => useContext(ModelEditorContext);\n\nexport {\n  ModelEditor,\n  ModelEditorContext,\n  ModelEditButton,\n  EditableMultilineText,\n  EditableDateField,\n  useModelEditor,\n};\n"],"names":["createContext","useContext","h","EditButton","StatefulComponent","update","classNames","EditableText","Component","DateInput"],"mappings":"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"}