Skip to content

tkcrm/ui

Repository files navigation

Tailwind UI React Components

Simple and feature-rich tailwind react components

Documentation is under development

Installation

npm i @tkcrm/ui --save

Add styles and context

Add styles and context to your exist React project

import * as ReactDOM from "react-dom/client";
import { BrowserRouter as Router } from "react-router-dom";
import { ErrorBoundary, UIContext, UIContextClass } from "@tkcrm/ui";

import App from "./components/App";

// Routes are for breadcrumbs, where `routes: Route[]`
// but this is optional
import { routes } from "./routes";

import "./assets/css/style.css";

const rootElement = document.querySelector("#root");
if (rootElement) {
  const root = ReactDOM.createRoot(rootElement);
  root.render(
    <ErrorBoundary>
      <UIContext.Provider value={new UIContextClass({ routes })}>
        <Router>
          <App />
        </Router>
      </UIContext.Provider>
    </ErrorBoundary>
  );
}

You can import styles from your exist base css file

@import "@tkcrm/ui/dist/style.css";
// ...

Available components

  • Alert
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Dropdown
  • Errorboundary
  • Form
  • Link
  • Modal
  • Notification
  • Page
  • Preloader
  • Result
  • Spin
  • Table

Form fields

  • Number
  • Text
  • Textarea
  • Password
  • Email
  • Switch
  • Datetime
  • Time
  • Date
  • Month
  • Week

Examples

Alert

import { Alert } from "@tkcrm/ui";

const MyComponent: React.FC = () => {
  return (
    <>
      <Alert type="success" title="Some pretty title" text="Hello world!" />
    </>
  );
};

Notification

import { notification } from "@tkcrm/ui";

notification.error({
  title: "Error!"
  description: "Something went wrong",
  duration: 3000,
});

notification.success("Successfully created");

notification.warning("Hmm...");

notification.info({
    description: "Successfully created",
    image: "http://image_link/",
});

Modal

import { useState } from "react";
import { Modal, Button } from "@tkcrm/ui";

const MyComponent: React.FC = () => {
  const [showModal, setShowModal] = useState(false);
  const [loading, setLoading] = useState(false);

  return (
    <>
      <Modal
        show={showModal}
        onClose={setShowModal}
        type="error"
        footer={
          <>
            <Button
              style="error"
              rounded
              loading={loading}
              onClick={() => {
                setLoading(true);
                setTimeout(() => {
                  setLoading(false);
                  setShowModal(false);
                }, 3000);
              }}
              className="ml-3"
            >
              {t("delete")}
            </Button>
            <Button
              style="white"
              rounded
              border
              onClick={() => setShowModal(false)}
            >
              {t("cancel")}
            </Button>
          </>
        }
      >
        <Modal.Title>Delete user</Modal.Title>
        <div className="mt-2">
          <Modal.Description>
            Are you sure you want to delete this user? It will be impossible to
            undo this action.
          </Modal.Description>
        </div>
      </Modal>
    </>
  );
};

Form

You can use forms with any state manager, for example mobx-keystone

Define mobx-keystone model and a form configuration

import type { FormGroupProps } from "@tkcrm/ui";

import { model, Model, prop, tProp, types } from "mobx-keystone";

const userForm: FormGroupProps[] = [
  {
    title: "User info",
    fields: [
      {
        type: "text",
        name: ["firstName"],
        label: "First name",
        required: true,
        colSize: 6,
      },
      {
        type: "text",
        name: ["lastName"],
        label: "Last name",
        colSize: 6,
      },
      {
        type: "number",
        name: ["age"],
        label: "Age",
        colSize: 4,
      },
      {
        type: "date",
        name: ["birthday"],
        label: "Birthday",
        required: true,
        colSize: 4,
      },
      {
        type: "switch",
        name: ["isActive"],
        label: "Is Active",
        colSize: 4,
      },
    ],
  },
  {
    title: "Notification settings",
    fields: [
      {
        type: "switch",
        name: ["notifyEmail"],
        label: "Email",
        colSize: 4,
      },
      {
        type: "switch",
        name: ["notifySMS"],
        label: "SMS",
        colSize: 4,
      },
      {
        type: "switch",
        name: ["notifyPush"],
        label: "Push",
        colSize: 4,
      },
    ],
  },
];

const organizationForm: FormGroupProps[] = [
  {
    title: "Organization info",
    fields: [
      {
        type: "text",
        name: ["shortName"],
        label: "Short name",
        required: true,
        colSize: 6,
      },
      {
        type: "text",
        name: ["fullName"],
        label: "Full name",
        colSize: 6,
      },
      {
        type: "text",
        name: ["address"],
        label: "Address",
        colSize: 10,
      },
      {
        type: "switch",
        name: ["isActive"],
        label: "Is active",
        colSize: 2,
      },
    ],
  },
];

@model("frontend/user")
export class UserModel extends Model({
  firstName: tProp(types.string, "").withSetter(),
  lastName: tProp(types.string, "").withSetter(),
  age: tProp(types.number, 0).withSetter(),
  birthday: tProp(types.maybe(types.dateString)).withSetter(),
  isActive: tProp(types.boolean, false).withSetter(),
  notifyEmail: tProp(types.boolean, false).withSetter(),
  notifySMS: tProp(types.boolean, false).withSetter(),
  notifyPush: tProp(types.boolean, false).withSetter(),
}) {}

@model("frontend/users")
export class UsersModel extends Model({
  getResponse: prop<UserModel>().withSetter(),
}) {}

Add form component

import { observer } from "mobx-react-lite";
import { getSnapshot } from "mobx-keystone";
import {
  Form,
  Page,
  notification,
  getFormInstance,
  updateMobxKeystoneModelFields,
  FormInstance,
} from "@tkcrm/ui";

interface FormProps {
  instance: FormInstance;
  onSave?: (values: Record<string, any>) => void;
}

const FormControls: React.FC<FormProps> = observer(({ instance, onSave }) => {
  const handleUpdate = async (): Promise<boolean> => {
    return new Promise((r) => {
      setTimeout(() => {
        notification.success({ title: "Successfully updated" });
        r(true);
      }, 300);
    });
  };

  return (
    <div className="mt-4 flex justify-end">
      <Form.Reset instance={instance} className="mr-4">
        Reset
      </Form.Reset>
      <Form.Save
        instance={instance}
        loading={instance.isLoading}
        onSave={async (values) => {
          await handleUpdate();
          onSave?.(values);
        }}
      >
        Save
      </Form.Save>
    </div>
  );
});

const FormData: React.FC<FormProps> = ({ instance }) => {
  return (
    <>
      <Form instance={instance} />
      <FormControls instance={instance} />
    </>
  );
};

const Forms: React.FC = () => {
  const userModel = new UsersModel({
    getResponse: new UserModel({ firstName: "John" }),
  });

  let orgValues: Record<string, any> = {
    shortName: "LLC Google inc.",
    isActive: true,
  };

  const userFormInstance = getFormInstance(
    userForm,
    getSnapshot(userModel.getResponse),
    {
      formMessages: {
        form_validation_error_description: "Ошибка валидации формы",
      },
    }
  );

  const orgFormInstance = getFormInstance(organizationForm, orgValues);

  return (
    <Page.Wrapper max_width="lg">
      <Page.Heading title="Forms" />
      {/* Mobx Keystone example */}
      <FormData
        instance={userFormInstance}
        onSave={(values) => {
          updateMobxKeystoneModelFields(userModel.getResponse, values);
        }}
      />

      <div className="mt-8">
        {/* Example without state manager */}
        <FormData
          instance={orgFormInstance}
          onSave={(values) => {
            orgValues = values;
          }}
        />
      </div>
    </Page.Wrapper>
  );
};

Translate form messages

All form messages are available from here

import { FormMessages } from "@tkcrm/ui";

Add a new messages to a new form instance

const formInstance = getFormInstance(
  userForm,
  {},
  {
    formMessages: {
      form_validation_error_title: "Validating error!",
      form_validation_error_description: "Form validating error",
    },
  }
);