public commit
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import {
|
||||
Button,
|
||||
type ComboboxProps,
|
||||
Menu,
|
||||
Modal,
|
||||
NumberInput,
|
||||
Radio,
|
||||
SegmentedControl,
|
||||
Select,
|
||||
Switch,
|
||||
TagsInput,
|
||||
TextInput,
|
||||
Textarea,
|
||||
createTheme
|
||||
} from "@mantine/core";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
// default: https://github.com/mantinedev/mantine/blob/master/src/mantine-core/src/core/MantineProvider/default-theme.ts
|
||||
|
||||
export function createMantineTheme(scheme: "light" | "dark"): {
|
||||
theme: ReturnType<typeof createTheme>;
|
||||
forceColorScheme: "light" | "dark";
|
||||
} {
|
||||
const light = scheme === "light";
|
||||
const dark = !light;
|
||||
const baseComboboxProps: ComboboxProps = {
|
||||
offset: 2,
|
||||
transitionProps: { transition: "pop", duration: 75 }
|
||||
};
|
||||
|
||||
const input =
|
||||
"bg-muted/40 border-transparent disabled:bg-muted/50 disabled:text-primary/50 focus:border-zinc-500";
|
||||
|
||||
return {
|
||||
theme: createTheme({
|
||||
components: {
|
||||
Button: Button.extend({
|
||||
vars: (theme, props) => ({
|
||||
// https://mantine.dev/styles/styles-api/
|
||||
root: {
|
||||
"--button-height": "auto"
|
||||
}
|
||||
}),
|
||||
classNames: (theme, props) => ({
|
||||
root: twMerge("px-3 py-2 rounded-md h-auto")
|
||||
}),
|
||||
defaultProps: {
|
||||
size: "md",
|
||||
variant: light ? "filled" : "white"
|
||||
}
|
||||
}),
|
||||
Switch: Switch.extend({
|
||||
defaultProps: {
|
||||
size: "md",
|
||||
color: light ? "dark" : "blue"
|
||||
}
|
||||
}),
|
||||
Select: Select.extend({
|
||||
classNames: (theme, props) => ({
|
||||
//input: "focus:border-primary/50 bg-transparent disabled:text-primary",
|
||||
input,
|
||||
dropdown: `bknd-admin ${scheme} bg-background border-primary/20`
|
||||
}),
|
||||
defaultProps: {
|
||||
checkIconPosition: "right",
|
||||
comboboxProps: baseComboboxProps
|
||||
}
|
||||
}),
|
||||
TagsInput: TagsInput.extend({
|
||||
defaultProps: {
|
||||
comboboxProps: baseComboboxProps
|
||||
}
|
||||
}),
|
||||
Radio: Radio.extend({
|
||||
defaultProps: {
|
||||
classNames: {
|
||||
body: "items-center"
|
||||
}
|
||||
}
|
||||
}),
|
||||
TextInput: TextInput.extend({
|
||||
classNames: (theme, props) => ({
|
||||
wrapper: "leading-none",
|
||||
//input: "focus:border-primary/50 bg-transparent disabled:text-primary"
|
||||
input
|
||||
})
|
||||
}),
|
||||
NumberInput: NumberInput.extend({
|
||||
classNames: (theme, props) => ({
|
||||
wrapper: "leading-none",
|
||||
input
|
||||
})
|
||||
}),
|
||||
Textarea: Textarea.extend({
|
||||
classNames: (theme, props) => ({
|
||||
wrapper: "leading-none",
|
||||
input
|
||||
})
|
||||
}),
|
||||
Modal: Modal.extend({
|
||||
classNames: (theme, props) => ({
|
||||
...props.classNames,
|
||||
root: `bknd-admin ${scheme} ${props.className ?? ""} `,
|
||||
content: "bg-lightest border border-primary/10",
|
||||
overlay: "backdrop-blur"
|
||||
})
|
||||
}),
|
||||
Menu: Menu.extend({
|
||||
defaultProps: {
|
||||
offset: 2
|
||||
},
|
||||
|
||||
classNames: (theme, props) => ({
|
||||
dropdown: "!rounded-lg !px-1",
|
||||
item: "!rounded-md !text-[14px]"
|
||||
})
|
||||
}),
|
||||
SegmentedControl: SegmentedControl.extend({
|
||||
classNames: (theme, props) => ({
|
||||
root: light ? "bg-primary/5" : "bg-lightest/60",
|
||||
indicator: light ? "bg-background" : "bg-primary/15"
|
||||
})
|
||||
})
|
||||
},
|
||||
primaryColor: "dark",
|
||||
primaryShade: 9
|
||||
}),
|
||||
forceColorScheme: scheme
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { PrimaryFieldType } from "core";
|
||||
import { encodeSearch } from "core/utils";
|
||||
import { atom, useSetAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLocation } from "wouter";
|
||||
import { useBaseUrl } from "../client";
|
||||
import { useBknd } from "../client/BkndProvider";
|
||||
|
||||
export const routes = {
|
||||
data: {
|
||||
root: () => "/data",
|
||||
entity: {
|
||||
list: (entity: string) => `/entity/${entity}`,
|
||||
create: (entity: string) => `/entity/${entity}/create`,
|
||||
edit: (entity: string, id: PrimaryFieldType) => `/entity/${entity}/edit/${id}`
|
||||
},
|
||||
schema: {
|
||||
root: () => "/schema",
|
||||
entity: (entity: string) => `/schema/entity/${entity}`
|
||||
}
|
||||
},
|
||||
auth: {
|
||||
root: () => "/auth",
|
||||
users: {
|
||||
list: () => "/users",
|
||||
edit: (id: PrimaryFieldType) => `/users/edit/${id}`
|
||||
},
|
||||
roles: {
|
||||
list: () => "/roles",
|
||||
edit: (role: string) => `/roles/edit/${role}`
|
||||
},
|
||||
settings: () => "/settings",
|
||||
strategies: () => "/strategies"
|
||||
},
|
||||
flows: {
|
||||
root: () => "/flows",
|
||||
flows: {
|
||||
list: () => "/",
|
||||
edit: (id: PrimaryFieldType) => `/flow/${id}`
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
root: () => "/settings",
|
||||
path: (path: string[]) => `/settings/${path.join("/")}`
|
||||
}
|
||||
};
|
||||
|
||||
export function withQuery(url: string, query: object) {
|
||||
const search = encodeSearch(query, { encode: false });
|
||||
return `${url}?${search}`;
|
||||
}
|
||||
|
||||
export function withAbsolute(url: string) {
|
||||
const { app } = useBknd();
|
||||
const basepath = app.getAdminConfig().basepath;
|
||||
return `~/${basepath}/${url}`.replace(/\/+/g, "/");
|
||||
}
|
||||
|
||||
export function useNavigate() {
|
||||
const [location, navigate] = useLocation();
|
||||
const { app } = useBknd();
|
||||
const basepath = app.getAdminConfig().basepath;
|
||||
return [
|
||||
(
|
||||
url: string,
|
||||
options?: { query?: object; absolute?: boolean; replace?: boolean; state?: any }
|
||||
) => {
|
||||
const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url;
|
||||
navigate(options?.query ? withQuery(_url, options?.query) : _url, {
|
||||
replace: options?.replace,
|
||||
state: options?.state
|
||||
});
|
||||
},
|
||||
location
|
||||
] as const;
|
||||
}
|
||||
|
||||
export function useGoBack(
|
||||
fallback: string | (() => void) = "/",
|
||||
options?: {
|
||||
native?: boolean;
|
||||
absolute?: boolean;
|
||||
}
|
||||
) {
|
||||
const { app } = useBknd();
|
||||
const basepath = app.getAdminConfig().basepath;
|
||||
const [navigate] = useNavigate();
|
||||
const referrer = document.referrer;
|
||||
const history_length = window.history.length;
|
||||
const same = referrer.length === 0;
|
||||
const canGoBack = (same && history_length > 1) || !!same;
|
||||
|
||||
/*console.log("debug", {
|
||||
referrer,
|
||||
history_length,
|
||||
same,
|
||||
canGoBack
|
||||
});*/
|
||||
|
||||
function goBack() {
|
||||
if (same && history_length > 2) {
|
||||
//console.log("used history");
|
||||
window.history.back();
|
||||
} else {
|
||||
//console.log("used fallback");
|
||||
if (typeof fallback === "string") {
|
||||
const _fallback = options?.absolute
|
||||
? `~/${basepath}${fallback}`.replace(/\/+/g, "/")
|
||||
: fallback;
|
||||
//console.log("fallback", _fallback);
|
||||
|
||||
if (options?.native) {
|
||||
window.location.href = _fallback;
|
||||
} else {
|
||||
navigate(_fallback);
|
||||
}
|
||||
} else if (typeof fallback === "function") {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
same,
|
||||
canGoBack,
|
||||
goBack
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { type ClassNameValue, twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassNameValue[]) {
|
||||
return twMerge(inputs);
|
||||
}
|
||||
Reference in New Issue
Block a user