@povio/ui 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { createAclGuard } from './utils/vendor/acl/AclGuard';
2
+ export { AbilityContext } from './utils/vendor/acl/ability.context';
3
+ export type { AppAbilities, AppAbility } from './utils/vendor/acl/appAbility.types';
4
+ export { Can } from './utils/vendor/acl/Can';
5
+ export { AuthGuard } from './utils/vendor/auth/AuthGuard';
6
+ export { AuthContext } from './utils/vendor/auth/auth.context';
package/dist/auth.js ADDED
@@ -0,0 +1,12 @@
1
+ import { createAclGuard } from "./utils/vendor/acl/AclGuard.js";
2
+ import { AbilityContext } from "./utils/vendor/acl/ability.context.js";
3
+ import { Can } from "./utils/vendor/acl/Can.js";
4
+ import { AuthGuard } from "./utils/vendor/auth/AuthGuard.js";
5
+ import { AuthContext } from "./utils/vendor/auth/auth.context.js";
6
+ export {
7
+ AbilityContext,
8
+ AuthContext,
9
+ AuthGuard,
10
+ Can,
11
+ createAclGuard
12
+ };
@@ -28,7 +28,6 @@ declare const componentRegistry: {
28
28
  readonly dateTimePicker: <TFieldValues extends import('react-hook-form').FieldValues>({ fullIso, ...props }: import('../DateTime/DateTimePicker/DateTimePicker').ControlledDateTimePickerProps<TFieldValues>) => import("react/jsx-runtime").JSX.Element;
29
29
  readonly timePicker: <TFieldValues extends import('react-hook-form').FieldValues>(props: import('../DateTime/TimePicker/TimePicker').ControlledTimePickerProps<TFieldValues>) => import("react/jsx-runtime").JSX.Element;
30
30
  readonly dateRangePicker: <TFieldValues extends import('react-hook-form').FieldValues>({ fullIso, minValue, maxValue, ...props }: import('../DateTime/DateRangePicker/DateRangePicker').ControlledDateRangePickerProps<TFieldValues>) => import("react/jsx-runtime").JSX.Element;
31
- readonly textEditor: <TFieldValues extends import('react-hook-form').FieldValues>(props: import('../TextEditor/TextEditor').ControlledTextEditorProps<TFieldValues>) => import("react/jsx-runtime").JSX.Element;
32
31
  readonly unknown: null;
33
32
  };
34
33
  export type InputComponentRegistry = typeof componentRegistry;
@@ -12,7 +12,6 @@ import { Autocomplete } from "../Selection/Autocomplete/Autocomplete.js";
12
12
  import { QueryAutocomplete } from "../Selection/Autocomplete/QueryAutocomplete.js";
13
13
  import { Select } from "../Selection/Select/Select.js";
14
14
  import { Slider } from "../Slider/Slider.js";
15
- import { TextEditor } from "../TextEditor/TextEditor.js";
16
15
  import { Toggle } from "../Toggle/Toggle.js";
17
16
  import { Segment } from "../../segment/Segment.js";
18
17
  const componentRegistry = {
@@ -31,7 +30,6 @@ const componentRegistry = {
31
30
  dateTimePicker: DateTimePicker,
32
31
  timePicker: TimePicker,
33
32
  dateRangePicker: DateRangePicker,
34
- textEditor: TextEditor,
35
33
  unknown: null
36
34
  };
37
35
  function InputItem({ form, inputDef }) {
@@ -33,7 +33,7 @@ const SelectListBoxItem = ({
33
33
  isDisabled,
34
34
  className: clsx(
35
35
  selectListBoxItemClass,
36
- !isMultiple && "selected:bg-interactive-contained-primary-idle selected:text-interactive-text-secondary-idle-inverted",
36
+ !isMultiple && "selected:bg-interactive-contained-primary-idle selected:text-interactive-contained-primary-on-idle",
37
37
  isNewItem ? "text-interactive-text-primary-idle" : "text-interactive-text-secondary-idle"
38
38
  ),
39
39
  children: [
@@ -41,7 +41,7 @@ const SelectListBoxItem = ({
41
41
  CheckboxCheckmark,
42
42
  {
43
43
  variant: "default",
44
- className: "group-focus-visible:!outline-none"
44
+ className: "group-focus-visible:outline-none!"
45
45
  }
46
46
  ),
47
47
  isMultiple && isSearchable && /* @__PURE__ */ jsx(
@@ -54,7 +54,7 @@ const SelectListBoxItem = ({
54
54
  CheckboxCheckmark,
55
55
  {
56
56
  variant: "default",
57
- className: "group-focus-visible:!outline-none"
57
+ className: "group-focus-visible:outline-none!"
58
58
  }
59
59
  )
60
60
  }
@@ -1,20 +1,15 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { UrlObject } from 'url';
3
2
  interface UIRouterProviderProps {
4
- push: (url: UrlObject | string) => Promise<boolean>;
5
- replace: (url: UrlObject | string) => Promise<boolean>;
6
- query: NodeJS.Dict<string | string[]>;
3
+ push: (url: string) => Promise<boolean>;
4
+ replace: (url: string) => Promise<boolean>;
7
5
  pathname: string;
6
+ searchString: string;
8
7
  }
9
- interface UIRouterContextValue {
10
- push: (url: UrlObject | string) => Promise<boolean>;
11
- replace: (url: UrlObject | string) => Promise<boolean>;
12
- pathname: string;
13
- query: NodeJS.Dict<string | string[]>;
8
+ interface UIRouterContextValue extends UIRouterProviderProps {
14
9
  searchParams: URLSearchParams;
15
10
  }
16
11
  export declare namespace UIRouter {
17
- const UIRouterProvider: ({ children, pathname, push, query, replace, }: PropsWithChildren<UIRouterProviderProps>) => import("react/jsx-runtime").JSX.Element;
12
+ const UIRouterProvider: ({ children, pathname, push, replace, searchString, }: PropsWithChildren<UIRouterProviderProps>) => import("react/jsx-runtime").JSX.Element;
18
13
  const useUIRouter: () => UIRouterContextValue;
19
14
  }
20
15
  export {};
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { createContext, use } from "react";
2
+ import { createContext, useMemo, use } from "react";
3
3
  import { RouterProvider } from "react-aria";
4
4
  var UIRouter;
5
5
  ((UIRouter2) => {
@@ -8,27 +8,23 @@ var UIRouter;
8
8
  children,
9
9
  pathname,
10
10
  push,
11
- query,
12
- replace
11
+ replace,
12
+ searchString
13
13
  }) => {
14
- const searchParams = new URLSearchParams();
15
- for (const [k, v] of Object.entries(query)) {
16
- if (Array.isArray(v)) {
17
- for (const item of v) {
18
- searchParams.append(k, item);
19
- }
20
- } else {
21
- searchParams.set(k, v);
22
- }
23
- }
24
- const value = {
25
- searchParams,
26
- pathname,
27
- push,
28
- query,
29
- replace
30
- };
31
- return /* @__PURE__ */ jsx(UIRouterContext, { value, children: /* @__PURE__ */ jsx(RouterProvider, { navigate: (href) => push(href), children }) });
14
+ const searchParams = useMemo(() => {
15
+ return new URLSearchParams(searchString);
16
+ }, [searchString]);
17
+ const value = useMemo(
18
+ () => ({
19
+ searchParams,
20
+ pathname,
21
+ push,
22
+ searchString,
23
+ replace
24
+ }),
25
+ [searchParams, pathname, push, searchString, replace]
26
+ );
27
+ return /* @__PURE__ */ jsx(UIRouterContext, { value, children: /* @__PURE__ */ jsx(RouterProvider, { navigate: push, children }) });
32
28
  };
33
29
  UIRouter2.useUIRouter = () => {
34
30
  const context = use(UIRouterContext);
@@ -0,0 +1,17 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { z } from 'zod';
3
+ export declare namespace ThemeContext {
4
+ const ThemeSchema: z.ZodLiteral<"dark" | "light" | "system">;
5
+ export type Theme = z.infer<typeof ThemeSchema>;
6
+ interface ThemeContextValue {
7
+ theme: Theme;
8
+ systemTheme?: Exclude<Theme, "system">;
9
+ updateTheme: (theme: Theme) => void;
10
+ }
11
+ interface ThemeContextProviderProps {
12
+ storageKey?: string;
13
+ }
14
+ export const ThemeContextProvider: ({ children, storageKey, }: PropsWithChildren<ThemeContextProviderProps>) => import("react/jsx-runtime").JSX.Element;
15
+ export const useTheme: () => ThemeContextValue;
16
+ export {};
17
+ }
@@ -0,0 +1,69 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { createContext, useState, useCallback, useEffect, useMemo, use } from "react";
3
+ import { z } from "zod";
4
+ import { useLocalStorage } from "../hooks/useLocalStorage.js";
5
+ var ThemeContext;
6
+ ((_ThemeContext) => {
7
+ const DEFAULT_STORAGE_KEY = "theme";
8
+ const ThemeSchema = z.literal(["light", "dark", "system"]);
9
+ const ThemeContext2 = createContext(null);
10
+ _ThemeContext.ThemeContextProvider = ({
11
+ children,
12
+ storageKey = DEFAULT_STORAGE_KEY
13
+ }) => {
14
+ const [systemTheme, setSystemTheme] = useState(() => {
15
+ if (typeof window === "undefined") {
16
+ return void 0;
17
+ }
18
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
19
+ return media.matches ? "dark" : "light";
20
+ });
21
+ const { value: theme, set } = useLocalStorage({
22
+ key: storageKey,
23
+ schema: ThemeSchema
24
+ });
25
+ const updateTheme = useCallback(
26
+ (theme2) => {
27
+ set(theme2);
28
+ },
29
+ [set]
30
+ );
31
+ useEffect(() => {
32
+ if (typeof window === "undefined") {
33
+ return;
34
+ }
35
+ const onChange = (event) => {
36
+ setSystemTheme(event.matches ? "dark" : "light");
37
+ };
38
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
39
+ media.addEventListener("change", onChange);
40
+ return () => {
41
+ media.removeEventListener("change", onChange);
42
+ };
43
+ }, []);
44
+ useEffect(() => {
45
+ document.documentElement.classList.remove("dark");
46
+ if ((!theme || theme === "system") && systemTheme === "dark" || theme === "dark") {
47
+ document.documentElement.classList.add("dark");
48
+ } else if (theme === "light") {
49
+ document.documentElement.classList.add("light");
50
+ }
51
+ }, [theme, systemTheme]);
52
+ const contextValue = useMemo(
53
+ () => ({
54
+ theme: theme ?? "system",
55
+ systemTheme,
56
+ updateTheme
57
+ }),
58
+ [theme, systemTheme, updateTheme]
59
+ );
60
+ return /* @__PURE__ */ jsx(ThemeContext2.Provider, { value: contextValue, children });
61
+ };
62
+ _ThemeContext.useTheme = () => {
63
+ const context = use(ThemeContext2);
64
+ return context;
65
+ };
66
+ })(ThemeContext || (ThemeContext = {}));
67
+ export {
68
+ ThemeContext
69
+ };
@@ -6,7 +6,6 @@ import { ZodUtils } from '../utils/zod.utils';
6
6
  declare const defaultComponentTypes: {
7
7
  readonly datetime: "datePicker";
8
8
  readonly dateRange: "dateRangePicker";
9
- readonly textEditor: "textEditor";
10
9
  readonly boolean: "toggle";
11
10
  readonly number: "numberInput";
12
11
  readonly enum: "select";
@@ -21,7 +20,6 @@ export type DefaultComponentType<TZodType extends z.ZodType> = ZodUtils.ZodTypeS
21
20
  export type AllowedComponentType<TZodType extends z.ZodType> = ZodUtils.ZodTypeSwitch<TZodType, {
22
21
  datetime: "datePicker" | "dateTimePicker" | "timePicker";
23
22
  dateRange: "dateRangePicker";
24
- textEditor: "textEditor";
25
23
  boolean: "toggle" | "checkbox";
26
24
  number: "numberInput" | "slider" | "select" | "autocomplete" | "queryAutocomplete" | "segment";
27
25
  enum: "select" | "autocomplete" | "segment";
@@ -5,7 +5,6 @@ import { ZodUtils } from "../utils/zod.utils.js";
5
5
  const defaultComponentTypes = {
6
6
  datetime: "datePicker",
7
7
  dateRange: "dateRangePicker",
8
- textEditor: "textEditor",
9
8
  boolean: "toggle",
10
9
  number: "numberInput",
11
10
  enum: "select",
@@ -70,9 +69,6 @@ const getDefaultInputComponentType = (schemaType) => {
70
69
  if (ZodUtils.isDateRange(schemaType)) {
71
70
  return defaultComponentTypes.dateRange;
72
71
  }
73
- if (ZodUtils.isTextEditor(schemaType)) {
74
- return defaultComponentTypes.textEditor;
75
- }
76
72
  const unwrappedType = ZodUtils.unwrapZodType(schemaType);
77
73
  const componentType = ZOD_TYPE_COMPONENT_TYPE.find(([zodType]) => unwrappedType instanceof zodType)?.[1];
78
74
  if (componentType) {
@@ -1,17 +1,22 @@
1
1
  import { useMemo } from "react";
2
2
  import { useMediaQuery } from "react-responsive";
3
+ let breakpoints = null;
3
4
  function getBreakpoints() {
5
+ if (breakpoints) {
6
+ return breakpoints;
7
+ }
4
8
  const cs = getComputedStyle(document.documentElement);
5
9
  const entries = ["sm", "md", "lg", "xl", "2xl"].map((k) => [k, cs.getPropertyValue(`--breakpoint-${k}`).trim()]).filter(([, v]) => !!v);
6
- return Object.fromEntries(entries);
10
+ breakpoints = Object.fromEntries(entries);
11
+ return breakpoints;
7
12
  }
8
13
  function useBreakpoint(breakpointKey) {
9
- const breakpoints = useMemo(() => getBreakpoints(), []);
10
- if (!breakpoints) {
14
+ const breakpoints2 = useMemo(() => getBreakpoints(), []);
15
+ if (!breakpoints2) {
11
16
  throw new Error("Tailwind config is missing theme.screens");
12
17
  }
13
18
  const bool = useMediaQuery({
14
- query: `(min-width: ${breakpoints[breakpointKey]})`
19
+ query: `(min-width: ${breakpoints2[breakpointKey]})`
15
20
  });
16
21
  return bool;
17
22
  }
@@ -1,11 +1,8 @@
1
1
  import { z } from 'zod';
2
- type FilterValue = string | string[] | boolean | number;
3
2
  export interface FilterStore<TFilterData> {
4
3
  filterData: TFilterData;
5
4
  setFilterValue: (data: Partial<TFilterData>) => void;
6
5
  getFilterValue: (keys: (keyof TFilterData)[]) => Partial<TFilterData>;
7
6
  clearAllFilters: () => void;
8
7
  }
9
- export declare function parseFilterFromQuery(query: Record<string, any>, schema?: z.ZodObject<any>): Record<string, FilterValue>;
10
- export declare function useFilters<TFilterData>(defaultFilterValues?: TFilterData, prefix?: string, schema?: z.ZodObject<any>): FilterStore<TFilterData>;
11
- export {};
8
+ export declare const useFilters: <TFilterData>(defaultFilterValues?: TFilterData, prefix?: string, schema?: z.ZodObject<any>) => FilterStore<TFilterData>;
@@ -1,8 +1,10 @@
1
- import { useNavigate, useLocation } from "@tanstack/react-router";
2
- import { useMemo, useRef, useState, useEffect } from "react";
1
+ import { useState, useEffect, useCallback, useMemo } from "react";
3
2
  import { z } from "zod";
3
+ import { UIRouter } from "../config/router.context.js";
4
4
  function getFieldType(schema, fieldKey) {
5
- if (!schema || !schema.shape) return "unknown";
5
+ if (!schema || !schema.shape) {
6
+ return "unknown";
7
+ }
6
8
  let fieldSchema = schema.shape[fieldKey];
7
9
  while (fieldSchema) {
8
10
  if (fieldSchema instanceof z.ZodOptional || fieldSchema instanceof z.ZodNullable) {
@@ -11,164 +13,148 @@ function getFieldType(schema, fieldKey) {
11
13
  break;
12
14
  }
13
15
  }
14
- if (!fieldSchema) return "unknown";
15
- if (fieldSchema instanceof z.ZodNumber) return "number";
16
- if (fieldSchema instanceof z.ZodBoolean) return "boolean";
17
- if (fieldSchema instanceof z.ZodString) return "string";
16
+ if (!fieldSchema) {
17
+ return "unknown";
18
+ }
19
+ if (fieldSchema instanceof z.ZodNumber) {
20
+ return "number";
21
+ }
22
+ if (fieldSchema instanceof z.ZodBoolean) {
23
+ return "boolean";
24
+ }
25
+ if (fieldSchema instanceof z.ZodString) {
26
+ return "string";
27
+ }
18
28
  return "unknown";
19
29
  }
20
- function serializeFiltersToQuery(filterData, prefix) {
30
+ const serializeFiltersToQuery = (filterData, prefix) => {
21
31
  const query = {};
22
32
  for (const [key, value] of Object.entries(filterData)) {
23
33
  if (value === null || value === void 0) {
24
34
  continue;
25
35
  }
36
+ const filterKey = `filter[${prefix && `${prefix}-`}${key}]`;
26
37
  if (Array.isArray(value) || typeof value === "object") {
27
- query[`filter[${prefix && `${prefix}-`}${key}]`] = JSON.stringify(value);
38
+ query[filterKey] = JSON.stringify(value);
28
39
  } else if (typeof value === "boolean") {
29
- query[`filter[${prefix && `${prefix}-`}${key}]`] = value ? "true" : "false";
30
- } else if (typeof value === "number") {
31
- query[`filter[${prefix && `${prefix}-`}${key}]`] = value.toString();
40
+ query[filterKey] = value ? "true" : "false";
32
41
  } else {
33
- query[`filter[${prefix && `${prefix}-`}${key}]`] = value;
42
+ query[filterKey] = value.toString();
34
43
  }
35
44
  }
36
45
  return query;
37
- }
38
- function parseFilterFromQuery(query, schema) {
46
+ };
47
+ const parseFilterFromQuery = (searchParams, schema) => {
39
48
  const filter = {};
40
- for (const [key, value] of Object.entries(query)) {
41
- const match = key.match(/^filter\[(.+?)\]$/);
42
- if (match) {
43
- const filterKey = match[1];
44
- const baseKey = filterKey.includes("-") ? filterKey.split("-").pop() || filterKey : filterKey;
45
- if (value.startsWith("[") && value.endsWith("]") || value.startsWith("{") && value.endsWith("}")) {
46
- filter[filterKey] = JSON.parse(value);
47
- } else if (["true", "false"].includes(value)) {
49
+ for (const [key, value] of searchParams.entries()) {
50
+ const match = /^filter\[(.+?)\]$/.exec(key);
51
+ if (!match) {
52
+ continue;
53
+ }
54
+ const filterKey = match[1];
55
+ const isArray = value.startsWith("[") && value.endsWith("]");
56
+ const isObject = value.startsWith("{") && value.endsWith("}");
57
+ const isNumber = !Number.isNaN(Number(value)) && value !== "";
58
+ const isBoolean = ["true", "false"].includes(value);
59
+ if (isArray || isObject || isNumber) {
60
+ filter[filterKey] = JSON.parse(value);
61
+ } else if (isBoolean) {
62
+ filter[filterKey] = value === "true";
63
+ } else {
64
+ const expectedType = getFieldType(schema, filterKey);
65
+ if (expectedType === "number" && !Number.isNaN(Number(value)) && value !== "" && value !== null) {
66
+ filter[filterKey] = Number(value);
67
+ } else if (expectedType === "boolean" && ["true", "false"].includes(value)) {
48
68
  filter[filterKey] = value === "true";
49
69
  } else {
50
- const expectedType = getFieldType(schema, baseKey);
51
- if (expectedType === "number" && !Number.isNaN(Number(value)) && value !== "" && value !== null) {
52
- filter[filterKey] = Number(value);
53
- } else if (expectedType === "boolean" && ["true", "false"].includes(value)) {
54
- filter[filterKey] = value === "true";
55
- } else {
56
- filter[filterKey] = value;
57
- }
70
+ filter[filterKey] = value;
58
71
  }
59
72
  }
60
73
  }
61
74
  return filter;
62
- }
63
- function useFilters(defaultFilterValues, prefix = "", schema) {
64
- const navigate = useNavigate();
65
- const location = useLocation();
66
- const queryObject = useMemo(
67
- () => Object.fromEntries(new URLSearchParams(location.searchStr || "").entries()),
68
- [location.searchStr]
75
+ };
76
+ const useFilters = (defaultFilterValues, prefix = "", schema) => {
77
+ const { searchParams, pathname, replace } = UIRouter.useUIRouter();
78
+ const [filterData, setFilterData] = useState(
79
+ () => parseFilterFromQuery(searchParams, schema)
69
80
  );
70
- const hasAppliedDefaultsRef = useRef(false);
71
- const lastPathnameRef = useRef(location.pathname);
72
- const queryFilters = parseFilterFromQuery(queryObject, schema);
73
- const [filterData, setFilterData] = useState(queryFilters);
74
81
  useEffect(() => {
75
- if (lastPathnameRef.current !== location.pathname) {
76
- hasAppliedDefaultsRef.current = false;
77
- lastPathnameRef.current = location.pathname;
82
+ const setUrlToDefaultFilters = () => {
83
+ const flatFilterQuery = serializeFiltersToQuery(defaultFilterValues, prefix);
84
+ const url = `${pathname}?${new URLSearchParams({
85
+ ...Object.fromEntries(searchParams.entries()),
86
+ ...flatFilterQuery
87
+ }).toString()}`;
88
+ replace(url);
89
+ };
90
+ const currentFilters = parseFilterFromQuery(searchParams, schema);
91
+ const hasFiltersSet = Object.keys(currentFilters).length > 0;
92
+ if (defaultFilterValues && !hasFiltersSet) {
93
+ setUrlToDefaultFilters();
78
94
  }
79
- }, [location.pathname]);
95
+ }, [defaultFilterValues, prefix, schema]);
80
96
  useEffect(() => {
81
- const currentQueryObject = Object.fromEntries(new URLSearchParams(location.searchStr || "").entries());
82
- const currentFilters = parseFilterFromQuery(currentQueryObject, schema);
83
- const shouldApplyDefaults = defaultFilterValues && Object.keys(currentFilters).length === 0 && !hasAppliedDefaultsRef.current;
84
- if (shouldApplyDefaults) {
85
- const flatFilterQuery = serializeFiltersToQuery(defaultFilterValues, prefix);
86
- const sp = new URLSearchParams(location.searchStr || "");
87
- let needsNavigation = false;
88
- Object.entries(flatFilterQuery).forEach(([k, v]) => {
89
- const currentValue = sp.get(k);
90
- if (Array.isArray(v)) {
91
- const currentValues = sp.getAll(k);
92
- if (JSON.stringify(currentValues.sort()) !== JSON.stringify(v.sort())) {
93
- needsNavigation = true;
94
- }
95
- } else {
96
- if (currentValue !== v) {
97
- needsNavigation = true;
98
- }
99
- }
100
- });
101
- if (needsNavigation) {
102
- Object.entries(flatFilterQuery).forEach(([k, v]) => {
103
- sp.delete(k);
104
- if (Array.isArray(v)) {
105
- v.forEach((val) => sp.append(k, val));
97
+ const newQueryFilters = parseFilterFromQuery(searchParams, schema);
98
+ setFilterData(newQueryFilters);
99
+ }, [searchParams, schema]);
100
+ const setFilterValue = useCallback(
101
+ (data2) => {
102
+ let newFilters = { ...filterData };
103
+ const isReset = Object.keys(data2).length === 0;
104
+ const isResetToDefault = data2 === defaultFilterValues;
105
+ if (isResetToDefault) {
106
+ newFilters = {
107
+ ...defaultFilterValues
108
+ };
109
+ } else if (isReset) {
110
+ newFilters = {};
111
+ } else {
112
+ Object.entries(data2).forEach(([key, value]) => {
113
+ if (value === void 0) {
114
+ delete newFilters[key];
106
115
  } else {
107
- sp.set(k, v);
116
+ newFilters[key] = value;
108
117
  }
109
118
  });
110
- const to = `${location.pathname}${sp.toString() ? `?${sp.toString()}` : ""}`;
111
- navigate({ to, replace: true });
112
119
  }
113
- hasAppliedDefaultsRef.current = true;
114
- }
115
- }, [defaultFilterValues, prefix, location.pathname, location.searchStr]);
116
- useEffect(() => {
117
- const newQueryFilters = parseFilterFromQuery(queryObject, schema);
118
- setFilterData(newQueryFilters);
119
- }, [queryObject, schema]);
120
- const setFilterValue = (data) => {
121
- const next = { ...filterData };
122
- const isReset = Object.keys(data).length === 0;
123
- const isResetToDefault = data === defaultFilterValues;
124
- if (isResetToDefault) {
125
- Object.keys(next).forEach((key) => delete next[key]);
126
- Object.entries(defaultFilterValues ?? {}).forEach(([key, value]) => {
127
- next[key] = value;
128
- });
129
- } else if (!isReset) {
130
- Object.entries(data).forEach(([key, value]) => {
131
- if (value === void 0) {
132
- delete next[key];
133
- } else {
134
- next[key] = value;
135
- }
120
+ const prefixPart = prefix ? `${prefix}-` : "";
121
+ const cleanedQuery = searchParams.keys().reduce(
122
+ (acc, k) => {
123
+ if (!k.startsWith(`filter[${prefixPart}`)) {
124
+ acc[k] = searchParams.get(k) ?? "";
125
+ }
126
+ return acc;
127
+ },
128
+ {}
129
+ );
130
+ const flatFilterQuery = serializeFiltersToQuery(newFilters, prefix);
131
+ const url = `${pathname}?${new URLSearchParams({
132
+ ...cleanedQuery,
133
+ ...flatFilterQuery
134
+ }).toString()}`;
135
+ replace(url);
136
+ setFilterData(newFilters);
137
+ },
138
+ [filterData, prefix, pathname, replace, defaultFilterValues, searchParams]
139
+ );
140
+ const getFilterValue = useCallback(
141
+ (keys) => {
142
+ const result = {};
143
+ keys.forEach((key) => {
144
+ result[key] = filterData[key];
136
145
  });
137
- } else {
138
- Object.keys(next).forEach((key) => delete next[key]);
139
- }
140
- const sp = new URLSearchParams(location.searchStr || "");
141
- Array.from(sp.keys()).forEach((key) => {
142
- if (key.startsWith(`filter[${prefix && `${prefix}-`}`)) {
143
- sp.delete(key);
144
- }
145
- });
146
- const flatFilterQuery = serializeFiltersToQuery(next, prefix);
147
- Object.entries(flatFilterQuery).forEach(([k, v]) => {
148
- sp.delete(k);
149
- if (Array.isArray(v)) {
150
- v.forEach((val) => sp.append(k, val));
151
- } else {
152
- sp.set(k, v);
153
- }
154
- });
155
- const to = `${location.pathname}${sp.toString() ? `?${sp.toString()}` : ""}`;
156
- navigate({ to, replace: true });
157
- setFilterData(next);
158
- };
159
- const getFilterValue = (keys) => {
160
- const result = {};
161
- keys.forEach((key) => {
162
- result[key] = filterData[key];
163
- });
164
- return result;
165
- };
166
- const clearAllFilters = () => {
146
+ return result;
147
+ },
148
+ [filterData]
149
+ );
150
+ const clearAllFilters = useCallback(() => {
167
151
  setFilterValue(defaultFilterValues ?? {});
168
- };
169
- return { filterData, setFilterValue, getFilterValue, clearAllFilters };
170
- }
152
+ }, [defaultFilterValues, setFilterValue]);
153
+ const data = useMemo(() => {
154
+ return { filterData, setFilterValue, getFilterValue, clearAllFilters };
155
+ }, [filterData, setFilterValue, getFilterValue, clearAllFilters]);
156
+ return data;
157
+ };
171
158
  export {
172
- parseFilterFromQuery,
173
159
  useFilters
174
160
  };
@@ -7,7 +7,5 @@ export declare const useLocalStorage: <ZOutput, ZInput>({ key, schema }: IProps<
7
7
  value: ZOutput | null;
8
8
  set: (newValue: ZInput) => void;
9
9
  remove: () => void;
10
- error: Error | null;
11
- isInitialLoading: boolean;
12
10
  };
13
11
  export {};
@@ -1,36 +1,27 @@
1
- import { useState, useEffect, useCallback } from "react";
1
+ import { useState, useCallback } from "react";
2
+ const getValue = (key, schema) => {
3
+ if (!localStorage) {
4
+ return null;
5
+ }
6
+ const lsValue = localStorage.getItem(key);
7
+ if (!lsValue) {
8
+ return null;
9
+ }
10
+ let jsonOrString;
11
+ try {
12
+ jsonOrString = JSON.parse(lsValue);
13
+ } catch {
14
+ jsonOrString = lsValue;
15
+ }
16
+ const parsedValue = schema.safeParse(jsonOrString);
17
+ if (parsedValue.success) {
18
+ return parsedValue.data;
19
+ } else {
20
+ return null;
21
+ }
22
+ };
2
23
  const useLocalStorage = ({ key, schema }) => {
3
- const [value, setValue] = useState(null);
4
- const [isInitialLoading, setIsInitialLoading] = useState(true);
5
- const [error, setError] = useState(null);
6
- useEffect(() => {
7
- if (!localStorage) {
8
- setValue(null);
9
- return;
10
- }
11
- try {
12
- const lsValue = localStorage.getItem(key);
13
- if (!lsValue) {
14
- setValue(null);
15
- return;
16
- }
17
- let jsonOrString;
18
- try {
19
- jsonOrString = JSON.parse(lsValue);
20
- } catch {
21
- jsonOrString = lsValue;
22
- }
23
- const parsedValue = schema.safeParse(jsonOrString);
24
- if (parsedValue.success) {
25
- setValue(parsedValue.data);
26
- } else {
27
- setError(parsedValue.error);
28
- setValue(null);
29
- }
30
- } finally {
31
- setIsInitialLoading(false);
32
- }
33
- }, [key, schema]);
24
+ const [value, setValue] = useState(() => getValue(key, schema));
34
25
  const set = useCallback(
35
26
  (newValue) => {
36
27
  if (!localStorage) {
@@ -56,7 +47,7 @@ const useLocalStorage = ({ key, schema }) => {
56
47
  localStorage.removeItem(key);
57
48
  setValue(null);
58
49
  }, [key]);
59
- return { value, set, remove, error, isInitialLoading };
50
+ return { value, set, remove };
60
51
  };
61
52
  export {
62
53
  useLocalStorage
@@ -5,7 +5,7 @@ const DEFAULT_STATE = {
5
5
  pageSize: 20
6
6
  };
7
7
  function usePagination(defaultPagination) {
8
- const { pathname, query, replace, searchParams } = UIRouter.useUIRouter();
8
+ const { pathname, replace, searchParams } = UIRouter.useUIRouter();
9
9
  const [pagination, setPagination] = useState(
10
10
  defaultPagination ?? {
11
11
  pageIndex: searchParams.has("page") ? Number.parseInt(searchParams.get("page") ?? "", 10) : DEFAULT_STATE.pageIndex,
@@ -13,15 +13,15 @@ function usePagination(defaultPagination) {
13
13
  }
14
14
  );
15
15
  useEffect(() => {
16
- const { page: _page, size: _size, ...queryParms } = query;
17
- replace({
18
- pathname,
19
- query: {
20
- ...queryParms,
21
- ...pagination.pageSize !== DEFAULT_STATE.pageSize ? { size: pagination.pageSize } : {},
22
- ...pagination.pageIndex !== DEFAULT_STATE.pageIndex ? { page: pagination.pageIndex } : {}
23
- }
24
- });
16
+ const params = new URLSearchParams(searchParams);
17
+ if (pagination.pageSize !== DEFAULT_STATE.pageSize) {
18
+ params.append("size", pagination.pageSize.toString());
19
+ }
20
+ if (pagination.pageIndex !== DEFAULT_STATE.pageIndex) {
21
+ params.append("page", pagination.pageIndex.toString());
22
+ }
23
+ const url = `${pathname}${params.size > 0 ? `?${params.toString()}` : ""}`;
24
+ replace(url);
25
25
  }, [pagination]);
26
26
  return {
27
27
  pagination,
@@ -1,7 +1,7 @@
1
1
  import { useState, useMemo, useEffect } from "react";
2
2
  import { UIRouter } from "../config/router.context.js";
3
3
  function useSorting(defaultSorting, prefix = "") {
4
- const { pathname, replace, searchParams, query } = UIRouter.useUIRouter();
4
+ const { pathname, replace, searchParams } = UIRouter.useUIRouter();
5
5
  const [sorting, setSorting] = useState(
6
6
  defaultSorting ?? searchParams.get(`order${prefix && `-${prefix}`}`)?.split(",").map((item) => {
7
7
  if (item.startsWith("-")) {
@@ -17,11 +17,13 @@ function useSorting(defaultSorting, prefix = "") {
17
17
  return sorting.map((field) => `${field.desc ? "-" : "+"}${field.id}`).join(",");
18
18
  }, [sorting]);
19
19
  useEffect(() => {
20
- const { sort: _sort, ...queryParms } = query;
21
- replace({
22
- pathname,
23
- query: { ...queryParms, ...order ? { [`order${prefix && `-${prefix}`}`]: order } : {} }
24
- });
20
+ const params = new URLSearchParams(searchParams);
21
+ params.delete("order");
22
+ if (order) {
23
+ params.append(`order${prefix && `-${prefix}`}`, order);
24
+ }
25
+ const url = `${pathname}${params.size > 0 ? `?${params.toString()}` : ""}`;
26
+ replace(url);
25
27
  }, [order]);
26
28
  return { sorting, setSorting, order };
27
29
  }
package/dist/index.d.ts CHANGED
@@ -95,8 +95,6 @@ export { Select } from './components/inputs/Selection/Select/Select';
95
95
  export type { ControlledSliderProps, SliderProps } from './components/inputs/Slider/Slider';
96
96
  export { Slider } from './components/inputs/Slider/Slider';
97
97
  export type { InputVariantProps } from './components/inputs/shared/input.cva';
98
- export type { ControlledTextEditorProps, TextEditorProps, TextEditorValue, } from './components/inputs/TextEditor/TextEditor';
99
- export { TextEditor } from './components/inputs/TextEditor/TextEditor';
100
98
  export type { ControlledToggleProps, ToggleProps } from './components/inputs/Toggle/Toggle';
101
99
  export { Toggle } from './components/inputs/Toggle/Toggle';
102
100
  export type { MenuProps } from './components/Menu/Menu';
@@ -157,6 +155,7 @@ export { Confirmation } from './config/confirmation.context';
157
155
  export { ns, resources } from './config/i18n';
158
156
  export { LinkContext } from './config/link.context';
159
157
  export { UIRouter } from './config/router.context';
158
+ export { ThemeContext } from './config/theme.context';
160
159
  export { UIConfig } from './config/uiConfig.context';
161
160
  export { UIStyle } from './config/uiStyle.context';
162
161
  export type { DynamicColumnsOptions } from './helpers/dynamicColumns';
@@ -198,12 +197,6 @@ export { QueriesUtils } from './utils/queries.utils';
198
197
  export { RestUtils } from './utils/rest.utils';
199
198
  export { RoutingUtils } from './utils/routing.utils';
200
199
  export { StringUtils } from './utils/string.utils';
201
- export { createAclGuard } from './utils/vendor/acl/AclGuard';
202
- export { AbilityContext } from './utils/vendor/acl/ability.context';
203
- export type { AppAbilities, AppAbility } from './utils/vendor/acl/appAbility.types';
204
- export { Can } from './utils/vendor/acl/Can';
205
- export { AuthGuard } from './utils/vendor/auth/AuthGuard';
206
- export { AuthContext } from './utils/vendor/auth/auth.context';
207
200
  export type { GeneralErrorCodes } from './utils/vendor/error-handling';
208
201
  export { ApplicationException, ErrorHandler, SharedErrorHandler } from './utils/vendor/error-handling';
209
202
  export type { RequestConfig, RequestInfo, Response, RestClient as IRestClient, } from './utils/vendor/rest-client.types';
package/dist/index.js CHANGED
@@ -64,7 +64,6 @@ import { Autocomplete } from "./components/inputs/Selection/Autocomplete/Autocom
64
64
  import { QueryAutocomplete } from "./components/inputs/Selection/Autocomplete/QueryAutocomplete.js";
65
65
  import { Select } from "./components/inputs/Selection/Select/Select.js";
66
66
  import { Slider } from "./components/inputs/Slider/Slider.js";
67
- import { TextEditor } from "./components/inputs/TextEditor/TextEditor.js";
68
67
  import { Toggle } from "./components/inputs/Toggle/Toggle.js";
69
68
  import { Menu } from "./components/Menu/Menu.js";
70
69
  import { MenuPopover } from "./components/Menu/MenuPopover.js";
@@ -96,6 +95,7 @@ import { Confirmation } from "./config/confirmation.context.js";
96
95
  import { ns, resources } from "./config/i18n.js";
97
96
  import { LinkContext } from "./config/link.context.js";
98
97
  import { UIRouter } from "./config/router.context.js";
98
+ import { ThemeContext } from "./config/theme.context.js";
99
99
  import { UIConfig } from "./config/uiConfig.context.js";
100
100
  import { UIStyle } from "./config/uiStyle.context.js";
101
101
  import { dynamicColumns } from "./helpers/dynamicColumns.js";
@@ -130,15 +130,9 @@ import { QueriesUtils } from "./utils/queries.utils.js";
130
130
  import { RestUtils } from "./utils/rest.utils.js";
131
131
  import { RoutingUtils } from "./utils/routing.utils.js";
132
132
  import { StringUtils } from "./utils/string.utils.js";
133
- import { createAclGuard } from "./utils/vendor/acl/AclGuard.js";
134
- import { AbilityContext } from "./utils/vendor/acl/ability.context.js";
135
- import { Can } from "./utils/vendor/acl/Can.js";
136
- import { AuthGuard } from "./utils/vendor/auth/AuthGuard.js";
137
- import { AuthContext } from "./utils/vendor/auth/auth.context.js";
138
133
  import { ApplicationException, ErrorHandler, SharedErrorHandler } from "./utils/vendor/error-handling.js";
139
134
  import { RestInterceptor } from "./utils/vendor/rest-interceptor.js";
140
135
  export {
141
- AbilityContext,
142
136
  ActionModal,
143
137
  Alert,
144
138
  AlignCenterIcon,
@@ -151,15 +145,12 @@ export {
151
145
  ArrowDropUpIcon,
152
146
  ArrowLeftIcon,
153
147
  ArrowRightIcon,
154
- AuthContext,
155
- AuthGuard,
156
148
  Autocomplete,
157
149
  BoldIcon,
158
150
  BottomSheet,
159
151
  BulletedListIcon,
160
152
  Button,
161
153
  CalendarIcon,
162
- Can,
163
154
  CellText,
164
155
  CheckIcon,
165
156
  Checkbox,
@@ -238,8 +229,8 @@ export {
238
229
  TextArea,
239
230
  TextButton,
240
231
  TextColorIcon,
241
- TextEditor,
242
232
  TextInput,
233
+ ThemeContext,
243
234
  TimePicker,
244
235
  Toast,
245
236
  ToastContainer,
@@ -255,7 +246,6 @@ export {
255
246
  ViewIcon,
256
247
  ViewOffIcon,
257
248
  compoundMapper,
258
- createAclGuard,
259
249
  dynamicColumns,
260
250
  dynamicInputs,
261
251
  isEqual,
@@ -0,0 +1,2 @@
1
+ export type { ControlledTextEditorProps, TextEditorProps, TextEditorValue, } from './components/inputs/TextEditor/TextEditor';
2
+ export { TextEditor } from './components/inputs/TextEditor/TextEditor';
@@ -0,0 +1,4 @@
1
+ import { TextEditor } from "./components/inputs/TextEditor/TextEditor.js";
2
+ export {
3
+ TextEditor
4
+ };
@@ -9,12 +9,10 @@ export declare namespace ZodUtils {
9
9
  type IsZodString<T> = IsZodType<T, z.ZodString>;
10
10
  type IsZodEmail<T> = IsZodType<T, z.ZodEmail>;
11
11
  type IsZodDateRange<T> = UnwrapZod<T> extends z.ZodObject ? UnwrapZod<UnwrapZod<T>["shape"]["start"]> extends z.ZodISODateTime ? UnwrapZod<UnwrapZod<T>["shape"]["end"]> extends z.ZodISODateTime ? true : false : false : false;
12
- type IsZodTextEditor<T> = UnwrapZod<T> extends z.ZodObject ? UnwrapZod<UnwrapZod<T>["shape"]["html"]> extends z.ZodString ? UnwrapZod<UnwrapZod<T>["shape"]["json"]> extends z.ZodObject<any> ? true : false : false : false;
13
12
  type IsZodObject<T> = IsZodType<T, z.ZodObject<any>>;
14
13
  type IsZodArray<T> = IsZodType<T, z.ZodArray<any>>;
15
- type ZodType<T> = IsZodType<T, z.ZodBoolean> extends true ? "boolean" : IsZodNumber<T> extends true ? "number" : IsZodEnum<T> extends true ? "enum" : IsZodDateTime<T> extends true ? "datetime" : IsZodUUID<T> extends true ? "uuid" : IsZodString<T> extends true ? "string" : IsZodEmail<T> extends true ? "email" : IsZodDateRange<T> extends true ? "dateRange" : IsZodTextEditor<T> extends true ? "textEditor" : IsZodObject<T> extends true ? "object" : IsZodArray<T> extends true ? "array" : never;
14
+ type ZodType<T> = IsZodType<T, z.ZodBoolean> extends true ? "boolean" : IsZodNumber<T> extends true ? "number" : IsZodEnum<T> extends true ? "enum" : IsZodDateTime<T> extends true ? "datetime" : IsZodUUID<T> extends true ? "uuid" : IsZodString<T> extends true ? "string" : IsZodEmail<T> extends true ? "email" : IsZodDateRange<T> extends true ? "dateRange" : IsZodObject<T> extends true ? "object" : IsZodArray<T> extends true ? "array" : never;
16
15
  type ZodTypeSwitch<TZodType extends z.ZodType, T extends Record<ZodType<TZodType> | "unknown", unknown>> = ZodType<TZodType> extends never ? never : T[ZodType<TZodType>];
17
16
  const unwrapZodType: (schemaType: z.core.$ZodType) => z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
18
17
  const isDateRange: (schemaType: z.core.$ZodType) => boolean;
19
- const isTextEditor: (schemaType: z.core.$ZodType) => boolean;
20
18
  }
@@ -15,10 +15,6 @@ var ZodUtils;
15
15
  const unwrappedType = (0, ZodUtils2.unwrapZodType)(schemaType);
16
16
  return unwrappedType instanceof z.ZodObject && hasZodObjectProperty(unwrappedType, "start", z.ZodISODateTime) && hasZodObjectProperty(unwrappedType, "end", z.ZodISODateTime);
17
17
  };
18
- ZodUtils2.isTextEditor = (schemaType) => {
19
- const unwrappedType = (0, ZodUtils2.unwrapZodType)(schemaType);
20
- return unwrappedType instanceof z.ZodObject && hasZodObjectProperty(unwrappedType, "html", z.ZodString) && hasZodObjectProperty(unwrappedType, "json", z.ZodObject);
21
- };
22
18
  })(ZodUtils || (ZodUtils = {}));
23
19
  export {
24
20
  ZodUtils
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@povio/ui",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "type": "module",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": "./dist/index.js",
9
- "./styles": "./dist/styles/index.css"
9
+ "./auth": "./dist/auth.js",
10
+ "./text-editor": "./dist/text-editor.js",
11
+ "./text-editor/styles": "./dist/styles/editor.css"
10
12
  },
11
13
  "files": [
12
14
  "dist"
@@ -14,6 +16,20 @@
14
16
  "homepage": "https://povio.com",
15
17
  "license": "UNLICENSED",
16
18
  "peerDependencies": {
19
+ "@casl/ability": "^6.7.3",
20
+ "@casl/react": "^5.0.0",
21
+ "@tiptap/core": "^2.26.3",
22
+ "@tiptap/extension-color": "^2.26.3",
23
+ "@tiptap/extension-heading": "^2.26.3",
24
+ "@tiptap/extension-highlight": "^2.26.3",
25
+ "@tiptap/extension-link": "^2.26.3",
26
+ "@tiptap/extension-placeholder": "^2.26.3",
27
+ "@tiptap/extension-text-align": "^2.26.3",
28
+ "@tiptap/extension-text-style": "^2.26.3",
29
+ "@tiptap/extension-underline": "^2.26.3",
30
+ "@tiptap/pm": "^2.26.3",
31
+ "@tiptap/react": "^2.26.3",
32
+ "@tiptap/starter-kit": "^2.26.3",
17
33
  "react": "^19.1.0",
18
34
  "react-dom": "^19.1.0"
19
35
  },
@@ -34,18 +50,6 @@
34
50
  "@react-types/shared": "^3.32.1",
35
51
  "@tanstack/react-query": "~5.85.9",
36
52
  "@tanstack/react-table": "^8.21.3",
37
- "@tiptap/core": "^2.26.3",
38
- "@tiptap/extension-color": "^2.26.3",
39
- "@tiptap/extension-heading": "^2.26.3",
40
- "@tiptap/extension-highlight": "^2.26.3",
41
- "@tiptap/extension-link": "^2.26.3",
42
- "@tiptap/extension-placeholder": "^2.26.3",
43
- "@tiptap/extension-text-align": "^2.26.3",
44
- "@tiptap/extension-text-style": "^2.26.3",
45
- "@tiptap/extension-underline": "^2.26.3",
46
- "@tiptap/pm": "^2.26.3",
47
- "@tiptap/react": "^2.26.3",
48
- "@tiptap/starter-kit": "^2.26.3",
49
53
  "axios": "^1.13.1",
50
54
  "class-variance-authority": "^0.7.1",
51
55
  "clsx": "^2.1.1",
@@ -1 +0,0 @@
1
- @import "./editor.css";