@pittorica/sheet-react 0.8.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.
@@ -0,0 +1,22 @@
1
+ export declare const overlay: string;
2
+ export declare const sheetRecipe: import('@vanilla-extract/recipes').RuntimeFn<{
3
+ side: {
4
+ bottom: string;
5
+ right: string;
6
+ left: string;
7
+ };
8
+ width: {
9
+ full: string;
10
+ small: string;
11
+ medium: string;
12
+ large: string;
13
+ xlarge: string;
14
+ xxlarge: string;
15
+ };
16
+ }>;
17
+ export declare const dragHandle: string;
18
+ export declare const header: string;
19
+ export declare const titleStyle: string;
20
+ export declare const content: string;
21
+ export declare const closeButton: string;
22
+ //# sourceMappingURL=sheet.css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet.css.d.ts","sourceRoot":"","sources":["../src/sheet.css.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,OAAO,QAMlB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;EAgFtB,CAAC;AAEH,eAAO,MAAM,UAAU,QASrB,CAAC;AAEH,eAAO,MAAM,MAAM,QASjB,CAAC;AAEH,eAAO,MAAM,UAAU,QAMrB,CAAC;AAEH,eAAO,MAAM,OAAO,QAOlB,CAAC;AAEH,eAAO,MAAM,WAAW,QAgBtB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@pittorica/sheet-react",
3
+ "version": "0.8.2",
4
+ "private": false,
5
+ "author": {
6
+ "name": "Davide Di Criscito"
7
+ },
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "main": "./dist/index.js",
17
+ "dependencies": {
18
+ "@tabler/icons-react": "^3.36.1",
19
+ "@vanilla-extract/css": "^1.18.0",
20
+ "@vanilla-extract/recipes": "^0.5.7",
21
+ "clsx": "^2.1.1",
22
+ "motion": "^12.29.0",
23
+ "react": "^19.2.3",
24
+ "react-dom": "^19.2.3",
25
+ "@pittorica/styles": "0.8.2"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.0.10",
29
+ "@types/react": "^19.2.9",
30
+ "@types/react-dom": "^19.2.3",
31
+ "@vitejs/plugin-react": "^5.1.2",
32
+ "babel-plugin-react-compiler": "^1.0.0",
33
+ "typescript": "~5.9.3",
34
+ "vite": "npm:rolldown-vite@7.3.1",
35
+ "vite-plugin-dts": "^4.5.4"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc -b && vite build",
42
+ "dev": "vite",
43
+ "lint": "eslint .",
44
+ "preview": "vite preview"
45
+ }
46
+ }
package/src/Sheet.tsx ADDED
@@ -0,0 +1,163 @@
1
+ import React, { useEffect } from 'react';
2
+
3
+ import { createPortal } from 'react-dom';
4
+
5
+ import clsx from 'clsx';
6
+
7
+ import { IconX } from '@tabler/icons-react';
8
+
9
+ import { AnimatePresence, motion, type PanInfo } from 'motion/react';
10
+ import type { RecipeVariants } from '@vanilla-extract/recipes';
11
+
12
+ import {
13
+ closeButton,
14
+ content,
15
+ dragHandle,
16
+ header,
17
+ overlay,
18
+ sheetRecipe,
19
+ titleStyle,
20
+ } from './sheet.css.js';
21
+
22
+ type SheetVariants = RecipeVariants<typeof sheetRecipe>;
23
+
24
+ export type SheetProps = {
25
+ isOpen: boolean;
26
+ onClose: () => void;
27
+
28
+ side?: NonNullable<SheetVariants>['side'];
29
+
30
+ maxWidth?: NonNullable<SheetVariants>['width'];
31
+
32
+ /**
33
+ * Background color of the sheet.
34
+ */
35
+ color?: string;
36
+
37
+ /**
38
+ * Text color of the sheet content (including title and close button).
39
+ * Use this to ensure contrast when using a custom background color.
40
+ */
41
+ textColor?: string;
42
+
43
+ title?: React.ReactNode;
44
+ showHandle?: boolean;
45
+ children: React.ReactNode;
46
+ className?: string;
47
+ };
48
+
49
+ const sheetVariants = {
50
+ bottom: {
51
+ hidden: { y: '100%' },
52
+ visible: { y: 0 },
53
+ },
54
+ right: {
55
+ hidden: { x: '100%' },
56
+ visible: { x: 0 },
57
+ },
58
+ left: {
59
+ hidden: { x: '-100%' },
60
+ visible: { x: 0 },
61
+ },
62
+ };
63
+
64
+ export const Sheet: React.FC<SheetProps> = ({
65
+ isOpen,
66
+ onClose,
67
+ side = 'right',
68
+ maxWidth = 'full',
69
+ color,
70
+ textColor,
71
+ title,
72
+ showHandle = true,
73
+ children,
74
+ className,
75
+ }) => {
76
+ useEffect(() => {
77
+ const handleEsc = (e: KeyboardEvent) => {
78
+ if (e.key === 'Escape') onClose();
79
+ };
80
+
81
+ if (isOpen) {
82
+ document.body.style.overflow = 'hidden';
83
+ globalThis.addEventListener('keydown', handleEsc);
84
+ }
85
+
86
+ return () => {
87
+ globalThis.removeEventListener('keydown', handleEsc);
88
+ };
89
+ }, [isOpen, onClose]);
90
+
91
+ const unlockScroll = () => {
92
+ document.body.style.overflow = '';
93
+ };
94
+
95
+ const handleDragEnd = (_: never, info: PanInfo) => {
96
+ if (info.offset.y > 100 || info.velocity.y > 500) {
97
+ onClose();
98
+ }
99
+ };
100
+
101
+ const appliedWidth = side === 'bottom' ? maxWidth : 'full';
102
+
103
+ return createPortal(
104
+ <AnimatePresence mode="wait" onExitComplete={unlockScroll}>
105
+ {isOpen && (
106
+ <>
107
+ <motion.div
108
+ className={overlay}
109
+ onClick={onClose}
110
+ aria-hidden="true"
111
+ initial={{ opacity: 0 }}
112
+ animate={{ opacity: 1 }}
113
+ exit={{ opacity: 0 }}
114
+ transition={{ duration: 0.2 }}
115
+ />
116
+
117
+ <motion.div
118
+ className={clsx(
119
+ sheetRecipe({ side, width: appliedWidth }),
120
+ className
121
+ )}
122
+ style={{
123
+ backgroundColor: color,
124
+ color: textColor,
125
+ }}
126
+ role="dialog"
127
+ aria-modal="true"
128
+ variants={sheetVariants[side]}
129
+ initial="hidden"
130
+ animate="visible"
131
+ exit="hidden"
132
+ transition={{
133
+ type: 'spring',
134
+ damping: 25,
135
+ stiffness: 300,
136
+ mass: 0.8,
137
+ }}
138
+ drag={side === 'bottom' ? 'y' : false}
139
+ dragConstraints={{ top: 0, bottom: 0 }}
140
+ dragElastic={{ top: 0, bottom: 0.2 }}
141
+ onDragEnd={handleDragEnd}
142
+ >
143
+ {side === 'bottom' && showHandle && <div className={dragHandle} />}
144
+
145
+ <div className={header}>
146
+ {title ? <h2 className={titleStyle}>{title}</h2> : <div />}
147
+ <button
148
+ className={closeButton}
149
+ onClick={onClose}
150
+ aria-label="Close sheet"
151
+ >
152
+ <IconX size={24} />
153
+ </button>
154
+ </div>
155
+
156
+ <div className={content}>{children}</div>
157
+ </motion.div>
158
+ </>
159
+ )}
160
+ </AnimatePresence>,
161
+ document.body
162
+ );
163
+ };
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './sheet.css.js';
2
+ export * from './Sheet.js';
@@ -0,0 +1,163 @@
1
+ import { pitto } from '@pittorica/styles';
2
+ import { style } from '@vanilla-extract/css';
3
+ import { recipe } from '@vanilla-extract/recipes';
4
+
5
+ const sizes = {
6
+ small: '40rem',
7
+ medium: '48rem',
8
+ large: '64rem',
9
+ xlarge: '80rem',
10
+ xxlarge: '96rem',
11
+ };
12
+
13
+ const zIndex = {
14
+ overlay: 10_000,
15
+ sheet: 10_001,
16
+ };
17
+
18
+ export const overlay = style({
19
+ position: 'fixed',
20
+ inset: 0,
21
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
22
+ zIndex: zIndex.overlay,
23
+ backdropFilter: 'blur(2px)',
24
+ });
25
+
26
+ export const sheetRecipe = recipe({
27
+ base: style({
28
+ position: 'fixed',
29
+ zIndex: zIndex.sheet,
30
+ display: 'flex',
31
+ flexDirection: 'column',
32
+ boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.15)',
33
+ overflow: 'hidden',
34
+ maxHeight: '100dvh',
35
+ willChange: 'transform',
36
+ boxSizing: 'border-box',
37
+ backgroundColor: pitto.color.brand[900].color,
38
+ // Default text color from theme, ensuring inheritance works if not overridden
39
+ color: pitto.color.brand[900].onColor,
40
+ }),
41
+
42
+ variants: {
43
+ side: {
44
+ bottom: style({
45
+ bottom: 0,
46
+ left: 0,
47
+ right: 0,
48
+ maxHeight: '90vh',
49
+ borderTopLeftRadius: '28px',
50
+ borderTopRightRadius: '28px',
51
+ }),
52
+ right: style({
53
+ top: 0,
54
+ right: 0,
55
+ bottom: 0,
56
+ width: '100%',
57
+ maxWidth: '400px',
58
+ borderTopLeftRadius: '28px',
59
+ borderBottomLeftRadius: '28px',
60
+ }),
61
+ left: style({
62
+ top: 0,
63
+ left: 0,
64
+ bottom: 0,
65
+ width: '100%',
66
+ maxWidth: '400px',
67
+ borderTopRightRadius: '28px',
68
+ borderBottomRightRadius: '28px',
69
+ }),
70
+ },
71
+
72
+ width: {
73
+ full: style({ width: '100%' }),
74
+ small: style({
75
+ width: '100%',
76
+ maxWidth: sizes.small,
77
+ marginInline: 'auto',
78
+ }),
79
+ medium: style({
80
+ width: '100%',
81
+ maxWidth: sizes.medium,
82
+ marginInline: 'auto',
83
+ }),
84
+ large: style({
85
+ width: '100%',
86
+ maxWidth: sizes.large,
87
+ marginInline: 'auto',
88
+ }),
89
+ xlarge: style({
90
+ width: '100%',
91
+ maxWidth: sizes.xlarge,
92
+ marginInline: 'auto',
93
+ }),
94
+ xxlarge: style({
95
+ width: '100%',
96
+ maxWidth: sizes.xxlarge,
97
+ marginInline: 'auto',
98
+ }),
99
+ },
100
+ },
101
+
102
+ defaultVariants: {
103
+ side: 'right',
104
+ width: 'full',
105
+ },
106
+ });
107
+
108
+ export const dragHandle = style({
109
+ width: '32px',
110
+ height: '4px',
111
+ backgroundColor: pitto.color.gray[300].color,
112
+ borderRadius: pitto.border.radius.full,
113
+ margin: '0 auto',
114
+ marginTop: pitto.spacing.medium,
115
+ marginBottom: pitto.spacing.small,
116
+ flexShrink: 0,
117
+ });
118
+
119
+ export const header = style({
120
+ paddingLeft: pitto.spacing.large,
121
+ paddingRight: pitto.spacing.large,
122
+ paddingTop: pitto.spacing.large,
123
+ paddingBottom: pitto.spacing.medium,
124
+ display: 'flex',
125
+ alignItems: 'center',
126
+ justifyContent: 'space-between',
127
+ flexShrink: 0,
128
+ });
129
+
130
+ export const titleStyle = style({
131
+ fontFamily: pitto.font.family.sans,
132
+ fontSize: pitto.font.size.headline.small,
133
+ fontWeight: pitto.font.weight.medium,
134
+ margin: 0,
135
+ color: 'inherit',
136
+ });
137
+
138
+ export const content = style({
139
+ paddingLeft: pitto.spacing.small,
140
+ paddingRight: pitto.spacing.small,
141
+ paddingBottom: pitto.spacing.xlarge,
142
+ flex: 1,
143
+ overflowY: 'auto',
144
+ paddingTop: 0,
145
+ });
146
+
147
+ export const closeButton = style({
148
+ background: 'transparent',
149
+ border: 'none',
150
+ cursor: 'pointer',
151
+ padding: pitto.spacing.small,
152
+ borderRadius: pitto.border.radius.full,
153
+ display: 'flex',
154
+ alignItems: 'center',
155
+ justifyContent: 'center',
156
+ color: 'inherit',
157
+ opacity: 0.7,
158
+ transition: 'background-color 0.2s, opacity 0.2s',
159
+ ':hover': {
160
+ backgroundColor: 'rgba(0,0,0, 0.05)', // Hover generico
161
+ opacity: 1,
162
+ },
163
+ });
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dist",
4
+ "rootDir": "src",
5
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
6
+ "target": "ES2022",
7
+ "useDefineForClassFields": true,
8
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
9
+ "module": "ESNext",
10
+ "types": ["vite/client"],
11
+ "skipLibCheck": true,
12
+
13
+ /* Bundler mode */
14
+ "moduleResolution": "bundler",
15
+ "allowImportingTsExtensions": false,
16
+ "verbatimModuleSyntax": true,
17
+ "moduleDetection": "force",
18
+ "noEmit": false,
19
+ "emitDeclarationOnly": true,
20
+ "declaration": true,
21
+ "declarationMap": true,
22
+ "jsx": "react-jsx",
23
+
24
+ /* Linting */
25
+ "strict": true,
26
+ "noUnusedLocals": true,
27
+ "noUnusedParameters": true,
28
+ "erasableSyntaxOnly": true,
29
+ "noFallthroughCasesInSwitch": true,
30
+ "noUncheckedSideEffectImports": true
31
+ },
32
+ "include": ["src"]
33
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": false,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["vite.config.ts"]
26
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,35 @@
1
+ import path from 'node:path';
2
+
3
+ import { defineConfig } from 'vite';
4
+ import dts from 'vite-plugin-dts';
5
+ import react from '@vitejs/plugin-react';
6
+
7
+ // https://vite.dev/config/
8
+ export default defineConfig({
9
+ plugins: [
10
+ react({
11
+ babel: {
12
+ plugins: [['babel-plugin-react-compiler']],
13
+ },
14
+ }),
15
+ dts({
16
+ insertTypesEntry: true,
17
+ include: ['src'],
18
+ rollupTypes: false,
19
+ outDir: 'dist',
20
+ tsconfigPath: './tsconfig.app.json',
21
+ }),
22
+ ],
23
+
24
+ build: {
25
+ lib: {
26
+ entry: path.resolve(import.meta.dirname, 'src/index.ts'),
27
+ name: '@pittorica/sheet-react',
28
+ formats: ['es', 'cjs'],
29
+ fileName: (format: string): string =>
30
+ `index.${format === 'es' ? 'js' : 'cjs'}`,
31
+ },
32
+ sourcemap: true,
33
+ minify: false,
34
+ },
35
+ });