@liiift-studio/mac-os9-ui 0.2.25 → 0.3.1
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/README.md +29 -2
- package/dist/base.css +99 -0
- package/dist/base.css.map +1 -0
- package/dist/index.cjs +81 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +7 -57
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +26 -0
- package/dist/index.js +81 -6
- package/dist/index.js.map +1 -1
- package/dist/types/components/Window/Window.d.ts +26 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -177,16 +177,43 @@ function MyComponent() {
|
|
|
177
177
|
|
|
178
178
|
## Styling
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
### Basic Setup
|
|
181
|
+
|
|
182
|
+
Import the component styles **once** in your application's entry point:
|
|
181
183
|
|
|
182
184
|
```tsx
|
|
183
185
|
// In your app's main file (e.g., main.tsx, _app.tsx, index.tsx)
|
|
184
186
|
import '@liiift-studio/mac-os9-ui/styles';
|
|
185
187
|
```
|
|
186
188
|
|
|
189
|
+
This provides:
|
|
190
|
+
- CSS custom properties (design tokens/variables)
|
|
191
|
+
- Font declarations (Pixel font family)
|
|
192
|
+
- Component styles
|
|
193
|
+
- Utility classes
|
|
194
|
+
|
|
187
195
|
This needs to be done only once at the root of your application. All components will then have the correct Mac OS 9 styles applied.
|
|
188
196
|
|
|
189
|
-
|
|
197
|
+
### Optional Global Styles
|
|
198
|
+
|
|
199
|
+
If you want the **full Mac OS 9 experience** with global styles applied to your entire application (body background, typography, box-sizing reset), you can optionally import the base styles:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// In your app's main file
|
|
203
|
+
import '@liiift-studio/mac-os9-ui/styles'; // Required
|
|
204
|
+
import '@liiift-studio/mac-os9-ui/base'; // Optional global styles
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The optional base styles include:
|
|
208
|
+
- Universal `box-sizing: border-box` reset
|
|
209
|
+
- Responsive typography scaling on `<html>`
|
|
210
|
+
- Body styles (margin, padding, font-family, colors)
|
|
211
|
+
|
|
212
|
+
**Note:** Only import `/base` if you want these global styles. The library is designed to work without polluting your application's global styles, making it easier to integrate into existing projects.
|
|
213
|
+
|
|
214
|
+
### CSS Modules
|
|
215
|
+
|
|
216
|
+
All components use CSS Modules internally, so styles are scoped and won't conflict with your application's CSS. The theme variables and component styles are extracted to separate CSS files for optimal caching and performance.
|
|
190
217
|
|
|
191
218
|
## TypeScript Support
|
|
192
219
|
|
package/dist/base.css
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Base Styles for Mac OS 9 UI
|
|
3
|
+
*
|
|
4
|
+
* This file contains global element styles (html, body, box-sizing reset)
|
|
5
|
+
* that create the full Mac OS 9 experience.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Only import this if you want these styles applied globally
|
|
8
|
+
* to your entire application.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import '@liiift-studio/mac-os9-ui/styles'; // Required
|
|
12
|
+
* import '@liiift-studio/mac-os9-ui/base'; // Optional global styles
|
|
13
|
+
*
|
|
14
|
+
* What this includes:
|
|
15
|
+
* - Universal box-sizing reset
|
|
16
|
+
* - Responsive typography scaling on <html>
|
|
17
|
+
* - Base body styles (margin, padding, font, color)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* ========================================
|
|
21
|
+
* Universal Box Sizing
|
|
22
|
+
* ======================================== */
|
|
23
|
+
|
|
24
|
+
*,
|
|
25
|
+
*::before,
|
|
26
|
+
*::after {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ========================================
|
|
31
|
+
* HTML Base Styles
|
|
32
|
+
* Mobile-first responsive typography
|
|
33
|
+
* ======================================== */
|
|
34
|
+
|
|
35
|
+
html {
|
|
36
|
+
/* Mobile/default: 14px base - optimal for mobile devices */
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
-webkit-font-smoothing: antialiased;
|
|
39
|
+
-moz-osx-font-smoothing: grayscale;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ========================================
|
|
43
|
+
* Responsive Typography
|
|
44
|
+
* Scale base font size at different breakpoints
|
|
45
|
+
* Following mobile-first best practices:
|
|
46
|
+
* - Mobile: 14px (better for small screens, maintains Mac OS 9 small text aesthetic)
|
|
47
|
+
* - Tablet: 16px (standard web default)
|
|
48
|
+
* - Desktop: 16px (comfortable for productivity)
|
|
49
|
+
* - Large Desktop: 18px (takes advantage of larger screens)
|
|
50
|
+
* ======================================== */
|
|
51
|
+
|
|
52
|
+
/* Small mobile: 480px+ */
|
|
53
|
+
|
|
54
|
+
@media (min-width: 480px) {
|
|
55
|
+
html {
|
|
56
|
+
font-size: 15px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Tablet: 768px+ */
|
|
61
|
+
|
|
62
|
+
@media (min-width: 768px) {
|
|
63
|
+
html {
|
|
64
|
+
font-size: 16px;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Desktop: 1024px+ */
|
|
69
|
+
|
|
70
|
+
@media (min-width: 1024px) {
|
|
71
|
+
html {
|
|
72
|
+
font-size: 16px;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Large Desktop/XL: 1440px+ */
|
|
77
|
+
|
|
78
|
+
@media (min-width: 1440px) {
|
|
79
|
+
html {
|
|
80
|
+
font-size: 18px;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* ========================================
|
|
85
|
+
* Body Base Styles
|
|
86
|
+
* Mac OS 9 default appearance
|
|
87
|
+
* ======================================== */
|
|
88
|
+
|
|
89
|
+
body {
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 0;
|
|
92
|
+
font-family: var(--font-system);
|
|
93
|
+
font-size: var(--font-size-md);
|
|
94
|
+
line-height: var(--line-height-normal);
|
|
95
|
+
color: var(--color-text);
|
|
96
|
+
background-color: var(--color-background);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/*# sourceMappingURL=base.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["base.css"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;EAiBE;;AAEF;;6CAE6C;;AAC7C;;;CAGC,sBAAsB;AACvB;;AAEA;;;6CAG6C;;AAC7C;CACC,2DAA2D;CAC3D,eAAe;CACf,mCAAmC;CACnC,kCAAkC;AACnC;;AAEA;;;;;;;;6CAQ6C;;AAE7C,yBAAyB;;AACzB;CACC;EACC,eAAe;CAChB;AACD;;AAEA,mBAAmB;;AACnB;CACC;EACC,eAAe;CAChB;AACD;;AAEA,qBAAqB;;AACrB;CACC;EACC,eAAe;CAChB;AACD;;AAEA,8BAA8B;;AAC9B;CACC;EACC,eAAe;CAChB;AACD;;AAEA;;;6CAG6C;;AAC7C;CACC,SAAS;CACT,UAAU;CACV,+BAA+B;CAC/B,8BAA8B;CAC9B,sCAAsC;CACtC,wBAAwB;CACxB,yCAAyC;AAC1C","file":"base.css","sourcesContent":["/**\n * Optional Base Styles for Mac OS 9 UI\n * \n * This file contains global element styles (html, body, box-sizing reset)\n * that create the full Mac OS 9 experience.\n * \n * IMPORTANT: Only import this if you want these styles applied globally\n * to your entire application.\n * \n * Usage:\n * import '@liiift-studio/mac-os9-ui/styles'; // Required\n * import '@liiift-studio/mac-os9-ui/base'; // Optional global styles\n * \n * What this includes:\n * - Universal box-sizing reset\n * - Responsive typography scaling on <html>\n * - Base body styles (margin, padding, font, color)\n */\n\n/* ========================================\n * Universal Box Sizing\n * ======================================== */\n*,\n*::before,\n*::after {\n\tbox-sizing: border-box;\n}\n\n/* ========================================\n * HTML Base Styles\n * Mobile-first responsive typography\n * ======================================== */\nhtml {\n\t/* Mobile/default: 14px base - optimal for mobile devices */\n\tfont-size: 14px;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n}\n\n/* ========================================\n * Responsive Typography\n * Scale base font size at different breakpoints\n * Following mobile-first best practices:\n * - Mobile: 14px (better for small screens, maintains Mac OS 9 small text aesthetic)\n * - Tablet: 16px (standard web default)\n * - Desktop: 16px (comfortable for productivity)\n * - Large Desktop: 18px (takes advantage of larger screens)\n * ======================================== */\n\n/* Small mobile: 480px+ */\n@media (min-width: 480px) {\n\thtml {\n\t\tfont-size: 15px;\n\t}\n}\n\n/* Tablet: 768px+ */\n@media (min-width: 768px) {\n\thtml {\n\t\tfont-size: 16px;\n\t}\n}\n\n/* Desktop: 1024px+ */\n@media (min-width: 1024px) {\n\thtml {\n\t\tfont-size: 16px;\n\t}\n}\n\n/* Large Desktop/XL: 1440px+ */\n@media (min-width: 1440px) {\n\thtml {\n\t\tfont-size: 18px;\n\t}\n}\n\n/* ========================================\n * Body Base Styles\n * Mac OS 9 default appearance\n * ======================================== */\nbody {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: var(--font-system);\n\tfont-size: var(--font-size-md);\n\tline-height: var(--line-height-normal);\n\tcolor: var(--color-text);\n\tbackground-color: var(--color-background);\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -818,14 +818,24 @@ var styles$6 = {"window":"Window-module_window","window--active":"Window-module_
|
|
|
818
818
|
* </Window>
|
|
819
819
|
* ```
|
|
820
820
|
*/
|
|
821
|
-
const Window = React.forwardRef(({ children, title, titleBar, active = true, width = 'auto', height = 'auto', className = '', contentClassName = '', classes, showControls = true, onClose, onMinimize, onMaximize, onMouseEnter, resizable = false, draggable = false, defaultPosition, position: controlledPosition, onPositionChange, }, ref) => {
|
|
821
|
+
const Window = React.forwardRef(({ children, title, titleBar, active = true, width = 'auto', height = 'auto', className = '', contentClassName = '', classes, showControls = true, onClose, onMinimize, onMaximize, onMouseEnter, resizable = false, minWidth = 200, minHeight = 100, maxWidth, maxHeight, onResize, draggable = false, defaultPosition, position: controlledPosition, onPositionChange, }, ref) => {
|
|
822
822
|
// Drag state management
|
|
823
823
|
const [internalPosition, setInternalPosition] = React.useState(defaultPosition || null);
|
|
824
824
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
825
825
|
const [hasBeenDragged, setHasBeenDragged] = React.useState(!!(defaultPosition || controlledPosition));
|
|
826
826
|
const dragStartRef = React.useRef(null);
|
|
827
|
+
// Resize state management
|
|
828
|
+
const [internalSize, setInternalSize] = React.useState({
|
|
829
|
+
width,
|
|
830
|
+
height,
|
|
831
|
+
});
|
|
832
|
+
const [isResizing, setIsResizing] = React.useState(false);
|
|
833
|
+
const resizeStartRef = React.useRef(null);
|
|
827
834
|
// Use controlled position if provided, otherwise use internal state
|
|
828
835
|
const currentPosition = controlledPosition || internalPosition;
|
|
836
|
+
// Use internal size state for resize tracking
|
|
837
|
+
const currentWidth = isResizing ? internalSize.width : width;
|
|
838
|
+
const currentHeight = isResizing ? internalSize.height : height;
|
|
829
839
|
// Handle mouse down on title bar to start dragging
|
|
830
840
|
const handleTitleBarMouseDown = React.useCallback((event) => {
|
|
831
841
|
if (!draggable)
|
|
@@ -850,6 +860,69 @@ const Window = React.forwardRef(({ children, title, titleBar, active = true, wid
|
|
|
850
860
|
};
|
|
851
861
|
setIsDragging(true);
|
|
852
862
|
}, [draggable]);
|
|
863
|
+
// Handle mouse down on resize handle to start resizing
|
|
864
|
+
const handleResizeMouseDown = React.useCallback((event) => {
|
|
865
|
+
if (!resizable)
|
|
866
|
+
return;
|
|
867
|
+
event.preventDefault();
|
|
868
|
+
event.stopPropagation();
|
|
869
|
+
const windowElement = event.currentTarget.closest(`.${styles$6.window}`);
|
|
870
|
+
if (!windowElement)
|
|
871
|
+
return;
|
|
872
|
+
const rect = windowElement.getBoundingClientRect();
|
|
873
|
+
// Store resize start info
|
|
874
|
+
resizeStartRef.current = {
|
|
875
|
+
width: rect.width,
|
|
876
|
+
height: rect.height,
|
|
877
|
+
mouseX: event.clientX,
|
|
878
|
+
mouseY: event.clientY,
|
|
879
|
+
};
|
|
880
|
+
setIsResizing(true);
|
|
881
|
+
}, [resizable]);
|
|
882
|
+
// Handle mouse move during resize
|
|
883
|
+
React.useEffect(() => {
|
|
884
|
+
if (!isResizing || !resizeStartRef.current)
|
|
885
|
+
return;
|
|
886
|
+
const handleMouseMove = (event) => {
|
|
887
|
+
event.preventDefault();
|
|
888
|
+
if (!resizeStartRef.current)
|
|
889
|
+
return;
|
|
890
|
+
// Calculate delta
|
|
891
|
+
const deltaX = event.clientX - resizeStartRef.current.mouseX;
|
|
892
|
+
const deltaY = event.clientY - resizeStartRef.current.mouseY;
|
|
893
|
+
// Calculate new size
|
|
894
|
+
let newWidth = resizeStartRef.current.width + deltaX;
|
|
895
|
+
let newHeight = resizeStartRef.current.height + deltaY;
|
|
896
|
+
// Apply constraints
|
|
897
|
+
if (newWidth < minWidth)
|
|
898
|
+
newWidth = minWidth;
|
|
899
|
+
if (newHeight < minHeight)
|
|
900
|
+
newHeight = minHeight;
|
|
901
|
+
if (maxWidth && newWidth > maxWidth)
|
|
902
|
+
newWidth = maxWidth;
|
|
903
|
+
if (maxHeight && newHeight > maxHeight)
|
|
904
|
+
newHeight = maxHeight;
|
|
905
|
+
// Update size
|
|
906
|
+
setInternalSize({
|
|
907
|
+
width: newWidth,
|
|
908
|
+
height: newHeight,
|
|
909
|
+
});
|
|
910
|
+
// Call callback if provided
|
|
911
|
+
if (onResize) {
|
|
912
|
+
onResize({ width: newWidth, height: newHeight });
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
const handleMouseUp = () => {
|
|
916
|
+
setIsResizing(false);
|
|
917
|
+
resizeStartRef.current = null;
|
|
918
|
+
};
|
|
919
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
920
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
921
|
+
return () => {
|
|
922
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
923
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
924
|
+
};
|
|
925
|
+
}, [isResizing, minWidth, minHeight, maxWidth, maxHeight, onResize]);
|
|
853
926
|
// Handle mouse move during drag
|
|
854
927
|
React.useEffect(() => {
|
|
855
928
|
if (!isDragging || !dragStartRef.current)
|
|
@@ -907,11 +980,13 @@ const Window = React.forwardRef(({ children, title, titleBar, active = true, wid
|
|
|
907
980
|
const titleBarClassNames = mergeClasses(styles$6.titleBar, draggable && styles$6['titleBar--draggable'], isDragging && styles$6['titleBar--dragging'], classes?.titleBar);
|
|
908
981
|
// Window style
|
|
909
982
|
const windowStyle = {};
|
|
910
|
-
|
|
911
|
-
|
|
983
|
+
// Apply width - use currentWidth during resize, otherwise use prop
|
|
984
|
+
if (currentWidth !== 'auto') {
|
|
985
|
+
windowStyle.width = typeof currentWidth === 'number' ? `${currentWidth}px` : currentWidth;
|
|
912
986
|
}
|
|
913
|
-
|
|
914
|
-
|
|
987
|
+
// Apply height - use currentHeight during resize, otherwise use prop
|
|
988
|
+
if (currentHeight !== 'auto') {
|
|
989
|
+
windowStyle.height = typeof currentHeight === 'number' ? `${currentHeight}px` : currentHeight;
|
|
915
990
|
}
|
|
916
991
|
// Apply position if draggable and has been dragged
|
|
917
992
|
if (draggable && hasBeenDragged && currentPosition) {
|
|
@@ -929,7 +1004,7 @@ const Window = React.forwardRef(({ children, title, titleBar, active = true, wid
|
|
|
929
1004
|
}
|
|
930
1005
|
return null;
|
|
931
1006
|
};
|
|
932
|
-
return (jsxRuntime.jsxs("div", { ref: ref, className: windowClassNames, style: windowStyle, onMouseEnter: onMouseEnter, children: [renderTitleBar(), jsxRuntime.jsx("div", { className: contentClassNames, children: children }), resizable && jsxRuntime.jsx("div", { className: styles$6.resizeHandle, "aria-hidden": "true" })] }));
|
|
1007
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: windowClassNames, style: windowStyle, onMouseEnter: onMouseEnter, children: [renderTitleBar(), jsxRuntime.jsx("div", { className: contentClassNames, children: children }), resizable && (jsxRuntime.jsx("div", { className: styles$6.resizeHandle, onMouseDown: handleResizeMouseDown, "aria-hidden": "true" }))] }));
|
|
933
1008
|
});
|
|
934
1009
|
Window.displayName = 'Window';
|
|
935
1010
|
|