@moraby/app-launcher 0.1.0 → 0.1.3
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/index.css +7 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +6 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
package/dist/index.css
CHANGED
|
@@ -128,6 +128,13 @@
|
|
|
128
128
|
background-color: #dadce0;
|
|
129
129
|
border-radius: 4px;
|
|
130
130
|
}
|
|
131
|
+
.app-launcher__footer {
|
|
132
|
+
display: flex;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
padding: 12px 16px;
|
|
135
|
+
border-top: 1px solid #e0e0e0;
|
|
136
|
+
background: #fafafa;
|
|
137
|
+
}
|
|
131
138
|
@media (max-width: 400px) {
|
|
132
139
|
.app-launcher__dropdown {
|
|
133
140
|
width: 280px;
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* App Launcher Styles */\r\n\r\n.app-launcher {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n/* Trigger Button */\r\n.app-launcher__trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 40px;\r\n height: 40px;\r\n border: none;\r\n background: transparent;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n transition: background-color 0.2s ease;\r\n}\r\n\r\n.app-launcher__trigger:hover {\r\n background-color: rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n.app-launcher__trigger:focus {\r\n outline: none;\r\n background-color: rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n.app-launcher__trigger-icon {\r\n font-size: 24px;\r\n color: #5f6368;\r\n}\r\n\r\n/* Dropdown */\r\n.app-launcher__dropdown {\r\n position: absolute;\r\n top: calc(100% + 8px);\r\n right: 0;\r\n width: 336px;\r\n max-height: 70vh;\r\n overflow-y: auto;\r\n background-color: #ffffff;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1), 0 8px 40px rgba(0, 0, 0, 0.2);\r\n z-index: 9999;\r\n animation: app-launcher-slide-in 0.15s ease-out;\r\n}\r\n\r\n@keyframes app-launcher-slide-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-8px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n/* Grid */\r\n.app-launcher__grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 4px;\r\n padding: 16px 8px;\r\n}\r\n\r\n/* Item */\r\n.app-launcher__item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 12px 8px;\r\n border: none;\r\n background: transparent;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.15s ease;\r\n min-height: 90px;\r\n}\r\n\r\n.app-launcher__item:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.app-launcher__item:focus {\r\n outline: none;\r\n background-color: rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n/* Icon */\r\n.app-launcher__icon-wrapper {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 48px;\r\n height: 48px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.app-launcher__icon {\r\n font-size: 32px;\r\n transition: transform 0.15s ease;\r\n}\r\n\r\n.app-launcher__item:hover .app-launcher__icon {\r\n transform: scale(1.1);\r\n}\r\n\r\n/* Name */\r\n.app-launcher__name {\r\n font-family: 'Google Sans', 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif;\r\n font-size: 13px;\r\n font-weight: 400;\r\n color: #3c4043;\r\n text-align: center;\r\n line-height: 1.3;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Loading & Error */\r\n.app-launcher__loading,\r\n.app-launcher__error {\r\n padding: 24px;\r\n text-align: center;\r\n font-size: 14px;\r\n color: #5f6368;\r\n}\r\n\r\n.app-launcher__error {\r\n color: #d93025;\r\n}\r\n\r\n/* Scrollbar */\r\n.app-launcher__dropdown::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n\r\n.app-launcher__dropdown::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n\r\n.app-launcher__dropdown::-webkit-scrollbar-thumb {\r\n background-color: #dadce0;\r\n border-radius: 4px;\r\n}\r\n\r\n/* Responsive */\r\n@media (max-width: 400px) {\r\n .app-launcher__dropdown {\r\n width: 280px;\r\n right: -8px;\r\n }\r\n\r\n .app-launcher__icon-wrapper {\r\n width: 40px;\r\n height: 40px;\r\n }\r\n\r\n .app-launcher__icon {\r\n font-size: 28px;\r\n }\r\n\r\n .app-launcher__name {\r\n font-size: 12px;\r\n }\r\n}\r\n"],"mappings":";AAEA,CAAC;AACC,YAAU;AACV,WAAS;AACT,eAAa;AACf;AAGA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,UAAQ;AACR,cAAY;AACZ,iBAAe;AACf,UAAQ;AACR,cAAY,iBAAiB,KAAK;AACpC;AAEA,CAbC,qBAaqB;AACpB,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAjBC,qBAiBqB;AACpB,WAAS;AACT,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAAC;AACC,aAAW;AACX,SAAO;AACT;AAGA,CAAC;AACC,YAAU;AACV,OAAK,KAAK,KAAK,EAAE;AACjB,SAAO;AACP,SAAO;AACP,cAAY;AACZ,cAAY;AACZ,oBAAkB;AAClB,iBAAe;AACf,cAAY,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpE,WAAS;AACT,aAAW,sBAAsB,MAAM;AACzC;AAEA,WAHa;AAIX;AACE,aAAS;AACT,eAAW,WAAW,MAAM,MAAM;AACpC;AACA;AACE,aAAS;AACT,eAAW,WAAW,GAAG,MAAM;AACjC;AACF;AAGA,CAAC;AACC,WAAS;AACT,yBAAuB,OAAO,CAAC,EAAE;AACjC,OAAK;AACL,WAAS,KAAK;AAChB;AAGA,CAAC;AACC,WAAS;AACT,kBAAgB;AAChB,eAAa;AACb,mBAAiB;AACjB,WAAS,KAAK;AACd,UAAQ;AACR,cAAY;AACZ,iBAAe;AACf,UAAQ;AACR,cAAY,iBAAiB,MAAM;AACnC,cAAY;AACd;AAEA,CAdC,kBAckB;AACjB,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAlBC,kBAkBkB;AACjB,WAAS;AACT,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAGA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,iBAAe;AACjB;AAEA,CAAC;AACC,aAAW;AACX,cAAY,UAAU,MAAM;AAC9B;AAEA,CAtCC,kBAsCkB,OAAO,CALzB;AAMC,aAAW,MAAM;AACnB;AAGA,CAAC;AACC;AAAA,IAAa,aAAa;AAAA,IAAE,QAAQ;AAAA,IAAE,aAAa;AAAA,IAAE,kBAAkB;AAAA,IAAE;AACzE,aAAW;AACX,eAAa;AACb,SAAO;AACP,cAAY;AACZ,eAAa;AACb,aAAW;AACX,YAAU;AACV,iBAAe;AACf,eAAa;AACf;AAGA,CAAC;AACD,CAAC;AACC,WAAS;AACT,cAAY;AACZ,aAAW;AACX,SAAO;AACT;AAEA,CAPC;AAQC,SAAO;AACT;AAGA,CAxGC,sBAwGsB;AACrB,SAAO;AACT;AAEA,CA5GC,sBA4GsB;AACrB,cAAY;AACd;AAEA,CAhHC,sBAgHsB;AACrB,oBAAkB;AAClB,iBAAe;AACjB;AAGA,QAAO,WAAY;AACjB,
|
|
1
|
+
{"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* App Launcher Styles */\r\n\r\n.app-launcher {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n/* Trigger Button */\r\n.app-launcher__trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 40px;\r\n height: 40px;\r\n border: none;\r\n background: transparent;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n transition: background-color 0.2s ease;\r\n}\r\n\r\n.app-launcher__trigger:hover {\r\n background-color: rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n.app-launcher__trigger:focus {\r\n outline: none;\r\n background-color: rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n.app-launcher__trigger-icon {\r\n font-size: 24px;\r\n color: #5f6368;\r\n}\r\n\r\n/* Dropdown */\r\n.app-launcher__dropdown {\r\n position: absolute;\r\n top: calc(100% + 8px);\r\n right: 0;\r\n width: 336px;\r\n max-height: 70vh;\r\n overflow-y: auto;\r\n background-color: #ffffff;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1), 0 8px 40px rgba(0, 0, 0, 0.2);\r\n z-index: 9999;\r\n animation: app-launcher-slide-in 0.15s ease-out;\r\n}\r\n\r\n@keyframes app-launcher-slide-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-8px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n/* Grid */\r\n.app-launcher__grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 4px;\r\n padding: 16px 8px;\r\n}\r\n\r\n/* Item */\r\n.app-launcher__item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 12px 8px;\r\n border: none;\r\n background: transparent;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.15s ease;\r\n min-height: 90px;\r\n}\r\n\r\n.app-launcher__item:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.app-launcher__item:focus {\r\n outline: none;\r\n background-color: rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n/* Icon */\r\n.app-launcher__icon-wrapper {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 48px;\r\n height: 48px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.app-launcher__icon {\r\n font-size: 32px;\r\n transition: transform 0.15s ease;\r\n}\r\n\r\n.app-launcher__item:hover .app-launcher__icon {\r\n transform: scale(1.1);\r\n}\r\n\r\n/* Name */\r\n.app-launcher__name {\r\n font-family: 'Google Sans', 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif;\r\n font-size: 13px;\r\n font-weight: 400;\r\n color: #3c4043;\r\n text-align: center;\r\n line-height: 1.3;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Loading & Error */\r\n.app-launcher__loading,\r\n.app-launcher__error {\r\n padding: 24px;\r\n text-align: center;\r\n font-size: 14px;\r\n color: #5f6368;\r\n}\r\n\r\n.app-launcher__error {\r\n color: #d93025;\r\n}\r\n\r\n/* Scrollbar */\r\n.app-launcher__dropdown::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n\r\n.app-launcher__dropdown::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n\r\n.app-launcher__dropdown::-webkit-scrollbar-thumb {\r\n background-color: #dadce0;\r\n border-radius: 4px;\r\n}\r\n\r\n/* Footer (for Settings button, etc.) */\r\n.app-launcher__footer {\r\n display: flex;\r\n justify-content: center;\r\n padding: 12px 16px;\r\n border-top: 1px solid #e0e0e0;\r\n background: #fafafa;\r\n}\r\n\r\n/* Responsive */\r\n@media (max-width: 400px) {\r\n .app-launcher__dropdown {\r\n width: 280px;\r\n right: -8px;\r\n }\r\n\r\n .app-launcher__icon-wrapper {\r\n width: 40px;\r\n height: 40px;\r\n }\r\n\r\n .app-launcher__icon {\r\n font-size: 28px;\r\n }\r\n\r\n .app-launcher__name {\r\n font-size: 12px;\r\n }\r\n}\r\n"],"mappings":";AAEA,CAAC;AACC,YAAU;AACV,WAAS;AACT,eAAa;AACf;AAGA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,UAAQ;AACR,cAAY;AACZ,iBAAe;AACf,UAAQ;AACR,cAAY,iBAAiB,KAAK;AACpC;AAEA,CAbC,qBAaqB;AACpB,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAjBC,qBAiBqB;AACpB,WAAS;AACT,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAAC;AACC,aAAW;AACX,SAAO;AACT;AAGA,CAAC;AACC,YAAU;AACV,OAAK,KAAK,KAAK,EAAE;AACjB,SAAO;AACP,SAAO;AACP,cAAY;AACZ,cAAY;AACZ,oBAAkB;AAClB,iBAAe;AACf,cAAY,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpE,WAAS;AACT,aAAW,sBAAsB,MAAM;AACzC;AAEA,WAHa;AAIX;AACE,aAAS;AACT,eAAW,WAAW,MAAM,MAAM;AACpC;AACA;AACE,aAAS;AACT,eAAW,WAAW,GAAG,MAAM;AACjC;AACF;AAGA,CAAC;AACC,WAAS;AACT,yBAAuB,OAAO,CAAC,EAAE;AACjC,OAAK;AACL,WAAS,KAAK;AAChB;AAGA,CAAC;AACC,WAAS;AACT,kBAAgB;AAChB,eAAa;AACb,mBAAiB;AACjB,WAAS,KAAK;AACd,UAAQ;AACR,cAAY;AACZ,iBAAe;AACf,UAAQ;AACR,cAAY,iBAAiB,MAAM;AACnC,cAAY;AACd;AAEA,CAdC,kBAckB;AACjB,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAEA,CAlBC,kBAkBkB;AACjB,WAAS;AACT,oBAAkB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAClC;AAGA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,iBAAe;AACjB;AAEA,CAAC;AACC,aAAW;AACX,cAAY,UAAU,MAAM;AAC9B;AAEA,CAtCC,kBAsCkB,OAAO,CALzB;AAMC,aAAW,MAAM;AACnB;AAGA,CAAC;AACC;AAAA,IAAa,aAAa;AAAA,IAAE,QAAQ;AAAA,IAAE,aAAa;AAAA,IAAE,kBAAkB;AAAA,IAAE;AACzE,aAAW;AACX,eAAa;AACb,SAAO;AACP,cAAY;AACZ,eAAa;AACb,aAAW;AACX,YAAU;AACV,iBAAe;AACf,eAAa;AACf;AAGA,CAAC;AACD,CAAC;AACC,WAAS;AACT,cAAY;AACZ,aAAW;AACX,SAAO;AACT;AAEA,CAPC;AAQC,SAAO;AACT;AAGA,CAxGC,sBAwGsB;AACrB,SAAO;AACT;AAEA,CA5GC,sBA4GsB;AACrB,cAAY;AACd;AAEA,CAhHC,sBAgHsB;AACrB,oBAAkB;AAClB,iBAAe;AACjB;AAGA,CAAC;AACC,WAAS;AACT,mBAAiB;AACjB,WAAS,KAAK;AACd,cAAY,IAAI,MAAM;AACtB,cAAY;AACd;AAGA,QAAO,WAAY;AACjB,GAhID;AAiIG,WAAO;AACP,WAAO;AACT;AAEA,GA3ED;AA4EG,WAAO;AACP,YAAQ;AACV;AAEA,GAvED;AAwEG,eAAW;AACb;AAEA,GAjED;AAkEG,eAAW;AACb;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import { IconType } from 'react-icons';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -42,6 +43,11 @@ interface AppLauncherProps {
|
|
|
42
43
|
* Called when an app is clicked
|
|
43
44
|
*/
|
|
44
45
|
onAppClick?: (app: AppItem) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Render custom footer content (e.g., Settings button)
|
|
48
|
+
* Used by admin app to add settings functionality
|
|
49
|
+
*/
|
|
50
|
+
renderFooter?: () => React.ReactNode;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* Internal app representation with resolved icon
|
|
@@ -66,7 +72,7 @@ interface ResolvedApp {
|
|
|
66
72
|
* // With direct apps array
|
|
67
73
|
* <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />
|
|
68
74
|
*/
|
|
69
|
-
declare function AppLauncher({ configUrl, apps: propApps, className, onAppClick, }: AppLauncherProps): react_jsx_runtime.JSX.Element;
|
|
75
|
+
declare function AppLauncher({ configUrl, apps: propApps, className, onAppClick, renderFooter, }: AppLauncherProps): react_jsx_runtime.JSX.Element;
|
|
70
76
|
|
|
71
77
|
/**
|
|
72
78
|
* Map of icon names to icon components
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import { IconType } from 'react-icons';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -42,6 +43,11 @@ interface AppLauncherProps {
|
|
|
42
43
|
* Called when an app is clicked
|
|
43
44
|
*/
|
|
44
45
|
onAppClick?: (app: AppItem) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Render custom footer content (e.g., Settings button)
|
|
48
|
+
* Used by admin app to add settings functionality
|
|
49
|
+
*/
|
|
50
|
+
renderFooter?: () => React.ReactNode;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* Internal app representation with resolved icon
|
|
@@ -66,7 +72,7 @@ interface ResolvedApp {
|
|
|
66
72
|
* // With direct apps array
|
|
67
73
|
* <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />
|
|
68
74
|
*/
|
|
69
|
-
declare function AppLauncher({ configUrl, apps: propApps, className, onAppClick, }: AppLauncherProps): react_jsx_runtime.JSX.Element;
|
|
75
|
+
declare function AppLauncher({ configUrl, apps: propApps, className, onAppClick, renderFooter, }: AppLauncherProps): react_jsx_runtime.JSX.Element;
|
|
70
76
|
|
|
71
77
|
/**
|
|
72
78
|
* Map of icon names to icon components
|
package/dist/index.js
CHANGED
|
@@ -116,7 +116,8 @@ function AppLauncher({
|
|
|
116
116
|
configUrl,
|
|
117
117
|
apps: propApps,
|
|
118
118
|
className,
|
|
119
|
-
onAppClick
|
|
119
|
+
onAppClick,
|
|
120
|
+
renderFooter
|
|
120
121
|
}) {
|
|
121
122
|
const [isOpen, setIsOpen] = (0, import_react.useState)(false);
|
|
122
123
|
const [apps, setApps] = (0, import_react.useState)([]);
|
|
@@ -183,7 +184,7 @@ function AppLauncher({
|
|
|
183
184
|
description: app.description
|
|
184
185
|
});
|
|
185
186
|
} else {
|
|
186
|
-
window.
|
|
187
|
+
window.open(app.url, "_blank", "noopener,noreferrer");
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `app-launcher ${className || ""}`, ref: containerRef, children: [
|
|
@@ -207,18 +208,13 @@ function AppLauncher({
|
|
|
207
208
|
onClick: () => handleAppClick(app),
|
|
208
209
|
title: app.description || app.name,
|
|
209
210
|
children: [
|
|
210
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__icon-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
211
|
-
app.icon,
|
|
212
|
-
{
|
|
213
|
-
className: "app-launcher__icon",
|
|
214
|
-
style: { color: app.color }
|
|
215
|
-
}
|
|
216
|
-
) }),
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__icon-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(app.icon, { className: "app-launcher__icon", style: { color: app.color } }) }),
|
|
217
212
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "app-launcher__name", children: app.name })
|
|
218
213
|
]
|
|
219
214
|
},
|
|
220
215
|
app.id
|
|
221
|
-
)) })
|
|
216
|
+
)) }),
|
|
217
|
+
renderFooter && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__footer", children: renderFooter() })
|
|
222
218
|
] })
|
|
223
219
|
] });
|
|
224
220
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/AppLauncher.tsx","../src/icons.ts"],"sourcesContent":["// Main component\r\nexport { AppLauncher, AppLauncher as default } from './AppLauncher';\r\n\r\n// Types\r\nexport type { \r\n AppLauncherProps, \r\n AppItem, \r\n AppLauncherConfig,\r\n ResolvedApp,\r\n} from './types';\r\n\r\n// Icons (in case consumers want to use them)\r\nexport { iconMap, getIcon } from './icons';\r\n","'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport {\r\n AppLauncherProps,\r\n AppItem,\r\n ResolvedApp,\r\n AppLauncherConfig,\r\n} from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (\r\n containerRef.current &&\r\n !containerRef.current.contains(event.target as Node)\r\n ) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: getIcon(app.icon),\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.icon.name || 'FaRocket',\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n window.location.href = app.url;\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className='app-launcher__trigger'\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label='Open app launcher'\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className='app-launcher__trigger-icon' />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className='app-launcher__dropdown'>\r\n {loading && <div className='app-launcher__loading'>Loading...</div>}\r\n\r\n {error && <div className='app-launcher__error'>{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className='app-launcher__grid'>\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className='app-launcher__item'\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className='app-launcher__icon-wrapper'>\r\n <app.icon\r\n className='app-launcher__icon'\r\n style={{ color: app.color }}\r\n />\r\n </div>\r\n <span className='app-launcher__name'>{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n"],"mappings":"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","names":["import_io5"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/AppLauncher.tsx","../src/icons.ts"],"sourcesContent":["// Main component\r\nexport { AppLauncher, AppLauncher as default } from './AppLauncher';\r\n\r\n// Types\r\nexport type { \r\n AppLauncherProps, \r\n AppItem, \r\n AppLauncherConfig,\r\n ResolvedApp,\r\n} from './types';\r\n\r\n// Icons (in case consumers want to use them)\r\nexport { iconMap, getIcon } from './icons';\r\n","'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport { AppLauncherProps, AppItem, ResolvedApp, AppLauncherConfig } from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n renderFooter,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: getIcon(app.icon),\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.icon.name || 'FaRocket',\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n // Open in new tab\r\n window.open(app.url, '_blank', 'noopener,noreferrer');\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className=\"app-launcher__trigger\"\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label=\"Open app launcher\"\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className=\"app-launcher__trigger-icon\" />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className=\"app-launcher__dropdown\">\r\n {loading && <div className=\"app-launcher__loading\">Loading...</div>}\r\n\r\n {error && <div className=\"app-launcher__error\">{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className=\"app-launcher__grid\">\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className=\"app-launcher__item\"\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className=\"app-launcher__icon-wrapper\">\r\n <app.icon className=\"app-launcher__icon\" style={{ color: app.color }} />\r\n </div>\r\n <span className=\"app-launcher__name\">{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Custom footer (e.g., Settings button) */}\r\n {renderFooter && <div className=\"app-launcher__footer\">{renderFooter()}</div>}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n"],"mappings":"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","names":["import_io5"]}
|
package/dist/index.mjs
CHANGED
|
@@ -128,7 +128,8 @@ function AppLauncher({
|
|
|
128
128
|
configUrl,
|
|
129
129
|
apps: propApps,
|
|
130
130
|
className,
|
|
131
|
-
onAppClick
|
|
131
|
+
onAppClick,
|
|
132
|
+
renderFooter
|
|
132
133
|
}) {
|
|
133
134
|
const [isOpen, setIsOpen] = useState(false);
|
|
134
135
|
const [apps, setApps] = useState([]);
|
|
@@ -195,7 +196,7 @@ function AppLauncher({
|
|
|
195
196
|
description: app.description
|
|
196
197
|
});
|
|
197
198
|
} else {
|
|
198
|
-
window.
|
|
199
|
+
window.open(app.url, "_blank", "noopener,noreferrer");
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
return /* @__PURE__ */ jsxs("div", { className: `app-launcher ${className || ""}`, ref: containerRef, children: [
|
|
@@ -219,18 +220,13 @@ function AppLauncher({
|
|
|
219
220
|
onClick: () => handleAppClick(app),
|
|
220
221
|
title: app.description || app.name,
|
|
221
222
|
children: [
|
|
222
|
-
/* @__PURE__ */ jsx("div", { className: "app-launcher__icon-wrapper", children: /* @__PURE__ */ jsx(
|
|
223
|
-
app.icon,
|
|
224
|
-
{
|
|
225
|
-
className: "app-launcher__icon",
|
|
226
|
-
style: { color: app.color }
|
|
227
|
-
}
|
|
228
|
-
) }),
|
|
223
|
+
/* @__PURE__ */ jsx("div", { className: "app-launcher__icon-wrapper", children: /* @__PURE__ */ jsx(app.icon, { className: "app-launcher__icon", style: { color: app.color } }) }),
|
|
229
224
|
/* @__PURE__ */ jsx("span", { className: "app-launcher__name", children: app.name })
|
|
230
225
|
]
|
|
231
226
|
},
|
|
232
227
|
app.id
|
|
233
|
-
)) })
|
|
228
|
+
)) }),
|
|
229
|
+
renderFooter && /* @__PURE__ */ jsx("div", { className: "app-launcher__footer", children: renderFooter() })
|
|
234
230
|
] })
|
|
235
231
|
] });
|
|
236
232
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/AppLauncher.tsx","../src/icons.ts"],"sourcesContent":["'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport {\r\n AppLauncherProps,\r\n AppItem,\r\n ResolvedApp,\r\n AppLauncherConfig,\r\n} from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (\r\n containerRef.current &&\r\n !containerRef.current.contains(event.target as Node)\r\n ) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: getIcon(app.icon),\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.icon.name || 'FaRocket',\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n window.location.href = app.url;\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className='app-launcher__trigger'\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label='Open app launcher'\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className='app-launcher__trigger-icon' />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className='app-launcher__dropdown'>\r\n {loading && <div className='app-launcher__loading'>Loading...</div>}\r\n\r\n {error && <div className='app-launcher__error'>{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className='app-launcher__grid'>\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className='app-launcher__item'\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className='app-launcher__icon-wrapper'>\r\n <app.icon\r\n className='app-launcher__icon'\r\n style={{ color: app.color }}\r\n />\r\n </div>\r\n <span className='app-launcher__name'>{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n"],"mappings":"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","names":["IoApps","IoApps"]}
|
|
1
|
+
{"version":3,"sources":["../src/AppLauncher.tsx","../src/icons.ts"],"sourcesContent":["'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport { AppLauncherProps, AppItem, ResolvedApp, AppLauncherConfig } from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n renderFooter,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: getIcon(app.icon),\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.icon.name || 'FaRocket',\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n // Open in new tab\r\n window.open(app.url, '_blank', 'noopener,noreferrer');\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className=\"app-launcher__trigger\"\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label=\"Open app launcher\"\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className=\"app-launcher__trigger-icon\" />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className=\"app-launcher__dropdown\">\r\n {loading && <div className=\"app-launcher__loading\">Loading...</div>}\r\n\r\n {error && <div className=\"app-launcher__error\">{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className=\"app-launcher__grid\">\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className=\"app-launcher__item\"\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className=\"app-launcher__icon-wrapper\">\r\n <app.icon className=\"app-launcher__icon\" style={{ color: app.color }} />\r\n </div>\r\n <span className=\"app-launcher__name\">{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Custom footer (e.g., Settings button) */}\r\n {renderFooter && <div className=\"app-launcher__footer\">{renderFooter()}</div>}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n"],"mappings":"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","names":["IoApps","IoApps"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moraby/app-launcher",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A Google-style app launcher component for React/Next.js applications",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsup",
|
|
21
21
|
"dev": "tsup --watch",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"version:patch": "npm version patch",
|
|
24
|
+
"version:minor": "npm version minor",
|
|
25
|
+
"version:major": "npm version major",
|
|
26
|
+
"release": "npm run build && npm publish --access public"
|
|
23
27
|
},
|
|
24
28
|
"peerDependencies": {
|
|
25
29
|
"react": ">=17.0.0",
|
|
@@ -29,8 +33,8 @@
|
|
|
29
33
|
"react-icons": "^5.0.0"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@types/react": "^
|
|
33
|
-
"@types/react-dom": "^
|
|
36
|
+
"@types/react": "^19",
|
|
37
|
+
"@types/react-dom": "^19",
|
|
34
38
|
"tsup": "^8.0.0",
|
|
35
39
|
"typescript": "^5.0.0"
|
|
36
40
|
},
|