From 5d7fdbb1677afdc813718bc5dbe7f12967b215e9 Mon Sep 17 00:00:00 2001 From: Dan Ichim Date: Tue, 30 Jun 2026 14:56:53 +0300 Subject: [PATCH 1/6] Add sample spotlight --- ui/spotlight-sandbox/.gitignore | 15 + ui/spotlight-sandbox/README.md | 21 ++ ui/spotlight-sandbox/eslint.config.js | 9 + ui/spotlight-sandbox/package-lock.json | 318 ++++++++++++++++++ ui/spotlight-sandbox/package.json | 39 +++ .../resources/ilibmanifest.json | 3 + ui/spotlight-sandbox/src/App/App.js | 41 +++ ui/spotlight-sandbox/src/App/App.module.less | 16 + ui/spotlight-sandbox/src/App/package.json | 3 + .../src/components/Barracuda.js | 103 ++++++ .../src/components/Barracuda.module.less | 36 ++ .../src/components/HoldButton.js | 29 ++ .../src/components/SampleItem/SampleItem.js | 26 ++ .../SampleItem/SampleItem.module.less | 36 ++ .../src/components/SampleItem/package.json | 3 + .../src/components/SpottableButton.js | 21 ++ .../components/SpottableButton.module.less | 46 +++ ui/spotlight-sandbox/src/index.js | 18 + ui/spotlight-sandbox/src/routes.js | 13 + .../src/styles/sampleTheme.less | 117 +++++++ .../src/views/ContainerSample.js | 71 ++++ .../src/views/ContainerSample.module.less | 58 ++++ .../src/views/DisappearSample.js | 140 ++++++++ .../src/views/DisappearSample.module.less | 87 +++++ ui/spotlight-sandbox/src/views/HoldSample.js | 96 ++++++ .../src/views/HoldSample.module.less | 37 ++ ui/spotlight-sandbox/src/views/MainView.js | 31 ++ .../src/views/MainView.module.less | 28 ++ ui/spotlight-sandbox/src/views/SamplePage.js | 33 ++ .../src/views/SamplePage.module.less | 16 + .../src/views/SandboxSample.js | 62 ++++ .../src/views/SandboxSample.module.less | 24 ++ ui/spotlight-sandbox/src/views/TestPage.js | 38 +++ .../src/views/TestPage.module.less | 61 ++++ ui/spotlight-sandbox/webos-meta/appinfo.json | 12 + 35 files changed, 1707 insertions(+) create mode 100644 ui/spotlight-sandbox/.gitignore create mode 100644 ui/spotlight-sandbox/README.md create mode 100644 ui/spotlight-sandbox/eslint.config.js create mode 100644 ui/spotlight-sandbox/package-lock.json create mode 100644 ui/spotlight-sandbox/package.json create mode 100644 ui/spotlight-sandbox/resources/ilibmanifest.json create mode 100644 ui/spotlight-sandbox/src/App/App.js create mode 100644 ui/spotlight-sandbox/src/App/App.module.less create mode 100644 ui/spotlight-sandbox/src/App/package.json create mode 100644 ui/spotlight-sandbox/src/components/Barracuda.js create mode 100644 ui/spotlight-sandbox/src/components/Barracuda.module.less create mode 100644 ui/spotlight-sandbox/src/components/HoldButton.js create mode 100644 ui/spotlight-sandbox/src/components/SampleItem/SampleItem.js create mode 100644 ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less create mode 100644 ui/spotlight-sandbox/src/components/SampleItem/package.json create mode 100644 ui/spotlight-sandbox/src/components/SpottableButton.js create mode 100644 ui/spotlight-sandbox/src/components/SpottableButton.module.less create mode 100644 ui/spotlight-sandbox/src/index.js create mode 100644 ui/spotlight-sandbox/src/routes.js create mode 100644 ui/spotlight-sandbox/src/styles/sampleTheme.less create mode 100644 ui/spotlight-sandbox/src/views/ContainerSample.js create mode 100644 ui/spotlight-sandbox/src/views/ContainerSample.module.less create mode 100644 ui/spotlight-sandbox/src/views/DisappearSample.js create mode 100644 ui/spotlight-sandbox/src/views/DisappearSample.module.less create mode 100644 ui/spotlight-sandbox/src/views/HoldSample.js create mode 100644 ui/spotlight-sandbox/src/views/HoldSample.module.less create mode 100644 ui/spotlight-sandbox/src/views/MainView.js create mode 100644 ui/spotlight-sandbox/src/views/MainView.module.less create mode 100644 ui/spotlight-sandbox/src/views/SamplePage.js create mode 100644 ui/spotlight-sandbox/src/views/SamplePage.module.less create mode 100644 ui/spotlight-sandbox/src/views/SandboxSample.js create mode 100644 ui/spotlight-sandbox/src/views/SandboxSample.module.less create mode 100644 ui/spotlight-sandbox/src/views/TestPage.js create mode 100644 ui/spotlight-sandbox/src/views/TestPage.module.less create mode 100644 ui/spotlight-sandbox/webos-meta/appinfo.json diff --git a/ui/spotlight-sandbox/.gitignore b/ui/spotlight-sandbox/.gitignore new file mode 100644 index 00000000..f94ea516 --- /dev/null +++ b/ui/spotlight-sandbox/.gitignore @@ -0,0 +1,15 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules + +# testing +coverage + +# production +build +dist + +# misc +.DS_Store +npm-debug.log diff --git a/ui/spotlight-sandbox/README.md b/ui/spotlight-sandbox/README.md new file mode 100644 index 00000000..3cabfc1d --- /dev/null +++ b/ui/spotlight-sandbox/README.md @@ -0,0 +1,21 @@ +## Spotlight Sandbox + +A sample Enact application that demonstrates standalone usage of `@enact/spotlight` for 5-way navigation and focus management. + +The sample includes: + +- **Container navigation** — nested spotlight containers with visual feedback when a container receives focus +- **Sandbox** — draggable and resizable spottable controls to exercise spatial navigation +- **Disappear sample** — focus behavior when controls are disabled, destroyed, or hidden +- **Hold sample** — press-and-hold Enter on a focused control to trigger hold and pulse events +- **Test page** — wide and tall controls for nearest-neighbor navigation testing + +Use the arrow keys on your keyboard to move focus between controls. Press Enter to activate a focused control. + +Each sample runs on its own page so 5-way navigation is not affected by other examples on screen. + +Run `npm install` then `npm run serve` to view the app at [http://localhost:8080](http://localhost:8080). + +--- + +This project was bootstrapped with the Enact [cli](https://github.com/enactjs/cli). diff --git a/ui/spotlight-sandbox/eslint.config.js b/ui/spotlight-sandbox/eslint.config.js new file mode 100644 index 00000000..65598d08 --- /dev/null +++ b/ui/spotlight-sandbox/eslint.config.js @@ -0,0 +1,9 @@ +module.exports = [ + { + ignores: [ + 'node_modules/*', + 'build/*', + 'dist/*' + ] + } +]; diff --git a/ui/spotlight-sandbox/package-lock.json b/ui/spotlight-sandbox/package-lock.json new file mode 100644 index 00000000..532390f8 --- /dev/null +++ b/ui/spotlight-sandbox/package-lock.json @@ -0,0 +1,318 @@ +{ + "name": "spotlight-sandbox", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "spotlight-sandbox", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@enact/core": "^5.4.2", + "@enact/i18n": "^5.4.2", + "@enact/spotlight": "^5.4.2", + "@enact/ui": "^5.4.2", + "ilib": "^14.21.2", + "prop-types": "^15.8.1", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router": "^7.13.1" + } + }, + "node_modules/@enact/core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@enact/core/-/core-5.5.1.tgz", + "integrity": "sha512-VA1xgb3b7SPH0M3tipVImo7xwStsxd/UOCaYrDwZQPzyAceRmMsPLUd6kSYy4sYwWfA0UR70XvktIhXreiLqkw==", + "license": "Apache-2.0", + "dependencies": { + "classnames": "^2.5.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "ramda": "^0.32.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-is": "^19.2.6", + "warning": "^4.0.3" + } + }, + "node_modules/@enact/i18n": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@enact/i18n/-/i18n-5.5.1.tgz", + "integrity": "sha512-QEyIOmL9G1zIxMgVbV3bPDrzcv4/mx0RXKqnklwb7QTmIqVyUrb9fedTU4ggpcFUfRD0Gf4MfFH+WdkwqfCSzQ==", + "license": "Apache-2.0", + "dependencies": { + "@enact/core": "^5.5.1", + "prop-types": "^15.8.1", + "ramda": "^0.32.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "xhr": "^2.6.0" + }, + "peerDependencies": { + "ilib": "^14.21.0 || ^14.21.0-webos1" + } + }, + "node_modules/@enact/spotlight": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@enact/spotlight/-/spotlight-5.5.1.tgz", + "integrity": "sha512-8LiFxQVVvCSWua0fs3wbvwXh6o9fRfc5u3gp9Z8tbksGzJpdZTrqb4YEUoRkOjOXzWrZv8YON6fjd7GPcFlKBw==", + "license": "Apache-2.0", + "dependencies": { + "@enact/core": "^5.5.1", + "classnames": "^2.5.1", + "prop-types": "^15.8.1", + "ramda": "^0.32.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "warning": "^4.0.3" + } + }, + "node_modules/@enact/ui": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@enact/ui/-/ui-5.5.1.tgz", + "integrity": "sha512-CjTCpklbCcfpEY91zrQPol8oMm/rf67wq9UqEmFSsw7kENqdOGaAR8Hcl1NDC8QmGmoneC/CtjsZUKznGf0zCA==", + "license": "Apache-2.0", + "dependencies": { + "@enact/core": "^5.5.1", + "@enact/i18n": "^5.5.1", + "classnames": "^2.5.1", + "ilib": "^14.22.0", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "ramda": "^0.32.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-is": "^19.2.6", + "warning": "^4.0.3" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "license": "MIT", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/ilib": { + "version": "14.22.0", + "resolved": "https://registry.npmjs.org/ilib/-/ilib-14.22.0.tgz", + "integrity": "sha512-N3Rj+B1BwVPjhl5z67lKfTepGYos9Yfx5dosRDnh5pmVKv69eHYXyN5MG2yv3FRS9ilygSE8DEC7fm6Yleng6A==", + "license": "Apache-2.0", + "engines": { + "node": ">=8 <25" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "license": "MIT", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "license": "MIT" + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/ramda": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.32.0.tgz", + "integrity": "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-is": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "license": "MIT", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/ui/spotlight-sandbox/package.json b/ui/spotlight-sandbox/package.json new file mode 100644 index 00000000..a7bcff37 --- /dev/null +++ b/ui/spotlight-sandbox/package.json @@ -0,0 +1,39 @@ +{ + "name": "spotlight-sandbox", + "version": "1.0.0", + "description": "An Enact sample demonstrating standalone @enact/spotlight 5-way navigation and focus.", + "author": "", + "main": "src/index.js", + "scripts": { + "serve": "enact serve", + "pack": "enact pack", + "pack-p": "enact pack -p", + "watch": "enact pack --watch", + "clean": "enact clean", + "lint": "enact lint --strict .", + "license": "enact license", + "test": "enact test", + "test-watch": "enact test --watch" + }, + "license": "Apache-2.0", + "private": true, + "repository": "https://github.com/enactjs/samples", + "enact": { + "title": "Spotlight Sandbox", + "isomorphic": true, + "ri": { + "baseSize": 24 + } + }, + "dependencies": { + "@enact/core": "^5.4.2", + "@enact/i18n": "^5.4.2", + "@enact/spotlight": "^5.4.2", + "@enact/ui": "^5.4.2", + "ilib": "^14.21.2", + "prop-types": "^15.8.1", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router": "^7.13.1" + } +} diff --git a/ui/spotlight-sandbox/resources/ilibmanifest.json b/ui/spotlight-sandbox/resources/ilibmanifest.json new file mode 100644 index 00000000..d946318d --- /dev/null +++ b/ui/spotlight-sandbox/resources/ilibmanifest.json @@ -0,0 +1,3 @@ +{ + "files": [] +} diff --git a/ui/spotlight-sandbox/src/App/App.js b/ui/spotlight-sandbox/src/App/App.js new file mode 100644 index 00000000..1684868c --- /dev/null +++ b/ui/spotlight-sandbox/src/App/App.js @@ -0,0 +1,41 @@ +import kind from '@enact/core/kind'; +import SpotlightRootDecorator from '@enact/spotlight/SpotlightRootDecorator'; +import {HashRouter, Route, Routes} from 'react-router'; + +import {sampleRoutes} from '../routes'; +import MainView from '../views/MainView'; +import SamplePage from '../views/SamplePage'; + +import css from './App.module.less'; + +const AppBase = kind({ + name: 'App', + + styles: { + css, + className: 'app' + }, + + render: (props) => ( +
+ + + } /> + {sampleRoutes.map(({path, title, Component}) => ( + + + + )} + /> + ))} + + +
+ ) +}); + +export default SpotlightRootDecorator(AppBase); diff --git a/ui/spotlight-sandbox/src/App/App.module.less b/ui/spotlight-sandbox/src/App/App.module.less new file mode 100644 index 00000000..59400c26 --- /dev/null +++ b/ui/spotlight-sandbox/src/App/App.module.less @@ -0,0 +1,16 @@ +:global { + html, + body, + #root { + height: 100%; + margin: 0; + overflow: hidden; + } +} + +.app { + height: 100%; + overflow: hidden; + position: relative; + background-color: #f1f5f9; +} diff --git a/ui/spotlight-sandbox/src/App/package.json b/ui/spotlight-sandbox/src/App/package.json new file mode 100644 index 00000000..bf7e4816 --- /dev/null +++ b/ui/spotlight-sandbox/src/App/package.json @@ -0,0 +1,3 @@ +{ + "main": "App.js" +} diff --git a/ui/spotlight-sandbox/src/components/Barracuda.js b/ui/spotlight-sandbox/src/components/Barracuda.js new file mode 100644 index 00000000..455d394a --- /dev/null +++ b/ui/spotlight-sandbox/src/components/Barracuda.js @@ -0,0 +1,103 @@ +import Spottable from '@enact/spotlight/Spottable'; +import Spotlight from '@enact/spotlight'; +import PropTypes from 'prop-types'; +import {useCallback, useRef, useState} from 'react'; +import css from './Barracuda.module.less'; + +const SpottableDiv = Spottable('div'); + +const CORNER_SIZE = 20; +const DEFAULT_SIZE = 50; + +const Barracuda = ({initialLeft = 0, initialTop = 0, label, ...rest}) => { + const [position, setPosition] = useState({left: initialLeft, top: initialTop}); + const [size, setSize] = useState({width: DEFAULT_SIZE, height: DEFAULT_SIZE}); + const dragState = useRef(null); + + const handlePointerDown = useCallback((ev) => { + if (ev.button !== 0) { + return; + } + + const bounds = ev.currentTarget.getBoundingClientRect(); + const relativeRight = bounds.right - ev.clientX; + const relativeBottom = bounds.bottom - ev.clientY; + const resizing = relativeRight < CORNER_SIZE && relativeBottom < CORNER_SIZE; + + dragState.current = { + resizing, + startX: ev.clientX, + startY: ev.clientY, + initialLeft: position.left, + initialTop: position.top, + initialWidth: size.width, + initialHeight: size.height + }; + + Spotlight.pause(); + ev.currentTarget.setPointerCapture(ev.pointerId); + }, [position.left, position.top, size.height, size.width]); + + const handlePointerMove = useCallback((ev) => { + if (!dragState.current) { + return; + } + + const {resizing, startX, startY, initialLeft: startLeft, initialTop: startTop, initialWidth, initialHeight} = dragState.current; + const dx = ev.clientX - startX; + const dy = ev.clientY - startY; + + if (resizing) { + setSize({ + width: Math.max(CORNER_SIZE, initialWidth + dx), + height: Math.max(CORNER_SIZE, initialHeight + dy) + }); + } else { + setPosition({ + left: startLeft + dx, + top: startTop + dy + }); + } + }, []); + + const handlePointerUp = useCallback((ev) => { + if (!dragState.current) { + return; + } + + dragState.current = null; + Spotlight.resume(); + + if (ev.currentTarget.hasPointerCapture(ev.pointerId)) { + ev.currentTarget.releasePointerCapture(ev.pointerId); + } + }, []); + + return ( + + {label} +
+ + ); +}; + +Barracuda.propTypes = { + initialLeft: PropTypes.number, + initialTop: PropTypes.number, + label: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) +}; + +export default Barracuda; diff --git a/ui/spotlight-sandbox/src/components/Barracuda.module.less b/ui/spotlight-sandbox/src/components/Barracuda.module.less new file mode 100644 index 00000000..f2d57a31 --- /dev/null +++ b/ui/spotlight-sandbox/src/components/Barracuda.module.less @@ -0,0 +1,36 @@ +@import '../styles/sampleTheme.less'; + +.barracuda { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #10b981; + border-radius: 10px; + background: + linear-gradient(180deg, rgba(16, 185, 129, 0.18) 0%, rgba(16, 185, 129, 0.08) 100%); + color: @text; + font-family: @mono; + font-size: 12px; + font-weight: 700; + user-select: none; + touch-action: none; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); + + &[data-spotlight-focused] { + border-color: @accent; + box-shadow: 0 0 0 3px @accent-soft; + } +} + +.corner { + position: absolute; + right: 0; + bottom: 0; + width: 18px; + height: 18px; + border: 1px solid @border; + border-radius: 8px 0 10px 0; + background-color: @surface-muted; + cursor: nwse-resize; +} diff --git a/ui/spotlight-sandbox/src/components/HoldButton.js b/ui/spotlight-sandbox/src/components/HoldButton.js new file mode 100644 index 00000000..ccbdcec1 --- /dev/null +++ b/ui/spotlight-sandbox/src/components/HoldButton.js @@ -0,0 +1,29 @@ +import kind from '@enact/core/kind'; +import Spottable from '@enact/spotlight/Spottable'; +import Touchable from '@enact/ui/Touchable'; +import compose from 'ramda/src/compose'; + +import css from './SpottableButton.module.less'; + +const HoldButtonBase = kind({ + name: 'HoldButton', + + styles: { + css, + className: 'button' + }, + + render: ({children, ...rest}) => ( +
+ {children} +
+ ) +}); + +// Touchable must wrap Spottable so Enter key emulation reaches Touchable's onMouseDown. +const HoldButton = compose( + Touchable, + Spottable +)(HoldButtonBase); + +export default HoldButton; diff --git a/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.js b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.js new file mode 100644 index 00000000..b9255b98 --- /dev/null +++ b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types'; +import {useCallback} from 'react'; + +import SpottableButton from '../SpottableButton'; + +import css from './SampleItem.module.less'; + +const SampleItem = ({children, navigate, path, ...rest}) => { + const handleSelect = useCallback(() => { + navigate(path); + }, [navigate, path]); + + return ( + + {children} + + ); +}; + +SampleItem.propTypes = { + children: PropTypes.node, + navigate: PropTypes.func, + path: PropTypes.string +}; + +export default SampleItem; diff --git a/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less new file mode 100644 index 00000000..5857330c --- /dev/null +++ b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less @@ -0,0 +1,36 @@ +@import '../../styles/sampleTheme.less'; + +.item { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + min-height: 56px; + padding: 14px 18px; + border: 1px solid @border; + border-radius: 14px; + background-color: @surface; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + font-size: 15px; + font-weight: 600; + text-align: left; + transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; + + &::after { + content: '→'; + color: @text-muted; + font-size: 18px; + transition: transform 160ms ease, color 160ms ease; + } + + &[data-spotlight-focused] { + border-color: @accent; + box-shadow: 0 0 0 3px @accent-soft; + transform: translateY(-1px); + + &::after { + color: @accent; + transform: translateX(4px); + } + } +} diff --git a/ui/spotlight-sandbox/src/components/SampleItem/package.json b/ui/spotlight-sandbox/src/components/SampleItem/package.json new file mode 100644 index 00000000..97462035 --- /dev/null +++ b/ui/spotlight-sandbox/src/components/SampleItem/package.json @@ -0,0 +1,3 @@ +{ + "main": "SampleItem.js" +} diff --git a/ui/spotlight-sandbox/src/components/SpottableButton.js b/ui/spotlight-sandbox/src/components/SpottableButton.js new file mode 100644 index 00000000..59db9037 --- /dev/null +++ b/ui/spotlight-sandbox/src/components/SpottableButton.js @@ -0,0 +1,21 @@ +import kind from '@enact/core/kind'; +import Spottable from '@enact/spotlight/Spottable'; + +import css from './SpottableButton.module.less'; + +const SpottableButton = Spottable(kind({ + name: 'SpottableButton', + + styles: { + css, + className: 'button' + }, + + render: ({children, disabled, ...rest}) => ( +
+ {children} +
+ ) +})); + +export default SpottableButton; diff --git a/ui/spotlight-sandbox/src/components/SpottableButton.module.less b/ui/spotlight-sandbox/src/components/SpottableButton.module.less new file mode 100644 index 00000000..7f8663ec --- /dev/null +++ b/ui/spotlight-sandbox/src/components/SpottableButton.module.less @@ -0,0 +1,46 @@ +@import '../styles/sampleTheme.less'; + +.button { + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: 88px; + min-height: 44px; + height: auto; + padding: 10px 16px; + margin: 0; + border: 1px solid @border; + border-radius: 10px; + background-color: @surface; + color: @text; + font-size: 14px; + font-weight: 500; + line-height: 1.3; + text-align: center; + cursor: pointer; + white-space: normal; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; + + &[data-spotlight-focused] { + border-color: @accent; + box-shadow: 0 0 0 3px @accent-soft; + transform: translateY(-1px); + } + + &[aria-disabled='true'] { + opacity: 0.45; + border-color: @border; + background-color: @surface-muted; + color: @text-muted; + cursor: not-allowed; + pointer-events: none; + transform: none; + + &[data-spotlight-focused] { + border-color: @border; + box-shadow: none; + } + } +} diff --git a/ui/spotlight-sandbox/src/index.js b/ui/spotlight-sandbox/src/index.js new file mode 100644 index 00000000..60509e6f --- /dev/null +++ b/ui/spotlight-sandbox/src/index.js @@ -0,0 +1,18 @@ +/* global ENACT_PACK_ISOMORPHIC */ +import {createRoot, hydrateRoot} from 'react-dom/client'; + +import App from './App'; + +const appElement = (); + +if (typeof window !== 'undefined') { + const container = document.getElementById('root'); + + if (ENACT_PACK_ISOMORPHIC) { + hydrateRoot(container, appElement); + } else { + createRoot(container).render(appElement); + } +} + +export default appElement; diff --git a/ui/spotlight-sandbox/src/routes.js b/ui/spotlight-sandbox/src/routes.js new file mode 100644 index 00000000..f2cf4844 --- /dev/null +++ b/ui/spotlight-sandbox/src/routes.js @@ -0,0 +1,13 @@ +import ContainerSample from './views/ContainerSample'; +import DisappearSample from './views/DisappearSample'; +import HoldSample from './views/HoldSample'; +import SandboxSample from './views/SandboxSample'; +import TestPage from './views/TestPage'; + +export const sampleRoutes = [ + {path: '/ContainerSample', title: 'Container Sample', Component: ContainerSample}, + {path: '/SandboxSample', title: 'Sandbox Sample', Component: SandboxSample}, + {path: '/DisappearSample', title: 'Disappear Sample', Component: DisappearSample}, + {path: '/HoldSample', title: 'Hold Sample', Component: HoldSample}, + {path: '/TestPage', title: 'Test Page', Component: TestPage} +]; diff --git a/ui/spotlight-sandbox/src/styles/sampleTheme.less b/ui/spotlight-sandbox/src/styles/sampleTheme.less new file mode 100644 index 00000000..9d001da9 --- /dev/null +++ b/ui/spotlight-sandbox/src/styles/sampleTheme.less @@ -0,0 +1,117 @@ +@surface: #fff; +@surface-muted: #f8fafc; +@border: #e2e8f0; +@text: #0f172a; +@text-muted: #64748b; +@accent: #6366f1; +@accent-soft: rgba(99, 102, 241, 0.12); +@container-active: #f43f5e; +@container-active-soft: rgba(244, 63, 94, 0.14); +@test-wide: #0ea5e9; +@test-wide-soft: rgba(14, 165, 233, 0.14); +@test-tall: #8b5cf6; +@test-tall-soft: rgba(139, 92, 246, 0.14); +@shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 8px 24px rgba(15, 23, 42, 0.06); +@mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + +.sampleRoot() { + max-width: 960px; +} + +.sampleDescription() { + margin: 0 0 20px; + font-size: 15px; + line-height: 1.6; + color: @text-muted; +} + +.sampleDemoPanel() { + padding: 24px; + border: 1px solid @border; + border-radius: 16px; + background: + radial-gradient(circle at top left, rgba(99, 102, 241, 0.08), transparent 42%), + linear-gradient(180deg, @surface-muted 0%, @surface 100%); + box-shadow: @shadow; +} + +.sampleStage() { + position: relative; + min-height: 360px; + border: 1px solid @border; + border-radius: 14px; + background-color: @surface; + box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.04); +} + +.spotlightContainer() { + border: 1px solid @border; + border-radius: 14px; + background-color: @surface; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + transition: border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease; + + &:focus-within { + border-color: @container-active; + background-color: #fff7f8; + box-shadow: + 0 0 0 3px @container-active-soft, + 0 10px 24px rgba(244, 63, 94, 0.08); + } +} + +.monoBadge() { + display: inline-flex; + align-self: flex-start; + padding: 4px 10px; + border-radius: 999px; + background-color: @accent-soft; + color: @accent; + font-family: @mono; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.statusBar() { + display: inline-flex; + align-items: center; + gap: 10px; + margin-top: 20px; + padding: 10px 14px; + border: 1px solid @border; + border-radius: 12px; + background-color: @surface; + box-shadow: @shadow; +} + +.statusLabel() { + font-size: 13px; + font-weight: 600; + color: @text-muted; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.statusValue() { + padding: 4px 10px; + border-radius: 8px; + background-color: @surface-muted; + color: @text; + font-family: @mono; + font-size: 14px; +} + +.statusCard() { + margin: 0; + padding: 12px 14px; + border: 1px solid @border; + border-radius: 12px; + background-color: @surface; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + color: @text-muted; + font-size: 14px; + line-height: 1.5; + list-style: none; +} diff --git a/ui/spotlight-sandbox/src/views/ContainerSample.js b/ui/spotlight-sandbox/src/views/ContainerSample.js new file mode 100644 index 00000000..42a8ab95 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/ContainerSample.js @@ -0,0 +1,71 @@ +import Spotlight from '@enact/spotlight'; +import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator'; +import {useCallback, useState} from 'react'; + +import SpottableButton from '../components/SpottableButton'; + +import css from './ContainerSample.module.less'; + +const Container = SpotlightContainerDecorator('div'); + +const ContainerSample = () => { + const [activeContainer, setActiveContainer] = useState(null); + + const handleFocusCapture = useCallback(() => { + setActiveContainer(Spotlight.getActiveContainer()); + }, []); + + const handleBlurCapture = useCallback((ev) => { + const layout = ev.currentTarget; + + window.requestAnimationFrame(() => { + if (!layout.contains(document.activeElement)) { + setActiveContainer(null); + } + }); + }, []); + + return ( +
+

+ Nested spotlight containers. Use arrow keys to move between buttons. + Containers with focused descendants are highlighted. +

+
+
+ + c1 +
+ c1b1 + c1b2 +
+
+ + c2 +
+ c2b1 + c2b2 + + c2c1 +
+ c2c1b1 + c2c1b2 +
+
+
+
+
+
+
+ Active container + {activeContainer ?? 'none'} +
+
+ ); +}; + +export default ContainerSample; diff --git a/ui/spotlight-sandbox/src/views/ContainerSample.module.less b/ui/spotlight-sandbox/src/views/ContainerSample.module.less new file mode 100644 index 00000000..997e1d8d --- /dev/null +++ b/ui/spotlight-sandbox/src/views/ContainerSample.module.less @@ -0,0 +1,58 @@ +@import '../styles/sampleTheme.less'; + +.containerSample { + .sampleRoot(); +} + +.description { + .sampleDescription(); +} + +.demoPanel { + .sampleDemoPanel(); +} + +.layout { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 20px; + align-items: start; +} + +.container { + position: relative; + display: flex; + flex-direction: column; + gap: 12px; + min-width: 0; + padding: 16px; + .spotlightContainer(); +} + +.nested { + flex: 1 1 100%; + margin-top: 4px; + background-color: @surface-muted; +} + +.containerLabel { + .monoBadge(); +} + +.controls { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.statusBar { + .statusBar(); +} + +.statusLabel { + .statusLabel(); +} + +.statusValue { + .statusValue(); +} diff --git a/ui/spotlight-sandbox/src/views/DisappearSample.js b/ui/spotlight-sandbox/src/views/DisappearSample.js new file mode 100644 index 00000000..47059379 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/DisappearSample.js @@ -0,0 +1,140 @@ +import Spotlight from '@enact/spotlight'; +import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator'; +import {useCallback, useLayoutEffect, useRef, useState} from 'react'; + +import SpottableButton from '../components/SpottableButton'; + +import css from './DisappearSample.module.less'; + +const Container = SpotlightContainerDecorator('div'); + +const DEFAULT_DISAPPEAR_TARGET = 'button02'; + +const DisappearSample = () => { + const [button1Disabled, setButton1Disabled] = useState(false); + const [showInnerButton2, setShowInnerButton2] = useState(true); + const [showInnerButton3, setShowInnerButton3] = useState(true); + const [showC11, setShowC11] = useState(true); + const [hideC11, setHideC11] = useState(false); + const pendingFocusRef = useRef(false); + + const focusDefaultDisappearTarget = useCallback(() => { + pendingFocusRef.current = true; + }, []); + + useLayoutEffect(() => { + if (pendingFocusRef.current) { + pendingFocusRef.current = false; + Spotlight.focus(DEFAULT_DISAPPEAR_TARGET); + } + }, [showC11, hideC11]); + + const handleRestore = useCallback(() => { + window.location.reload(); + }, []); + + const handleDisableButton1 = useCallback(() => { + setButton1Disabled(true); + }, []); + + const handleDestroyInnerButton2 = useCallback(() => { + setShowInnerButton2(false); + }, []); + + const handleHideInnerButton3 = useCallback(() => { + setShowInnerButton3(false); + }, []); + + const handleDestroyAncestor = useCallback(() => { + focusDefaultDisappearTarget(); + setShowC11(false); + }, [focusDefaultDisappearTarget]); + + const handleHideAncestor = useCallback(() => { + focusDefaultDisappearTarget(); + setHideC11(true); + }, [focusDefaultDisappearTarget]); + + return ( +
+

+ Exercises focus behavior when spottable controls are disabled, destroyed, or hidden. + "Destroy My Ancestor" and "Hide My Ancestor" move focus to the + second button above (spotlightId: button02) after the inner container disappears. +

+
+ +
+ + I am first spottable of the app + + + I am defaultSpotlightDisappear for "Destroy My Ancestor" + + + Restore disappeared buttons + +
+ {showC11 ? ( + + +
+
+ + Disable Me + +
+
+ {showInnerButton2 ? ( + + Destroy Me + + ) : null} +
+
+ + Hide Me + +
+
+
+
+ + Destroy My Ancestor + +
+
+ + Hide My Ancestor + +
+
+
+
+ ) : null} +
+
+
+ ); +}; + +export default DisappearSample; diff --git a/ui/spotlight-sandbox/src/views/DisappearSample.module.less b/ui/spotlight-sandbox/src/views/DisappearSample.module.less new file mode 100644 index 00000000..4b6abf58 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/DisappearSample.module.less @@ -0,0 +1,87 @@ +@import '../styles/sampleTheme.less'; + +.disappearSample { + .sampleRoot(); +} + +.description { + .sampleDescription(); +} + +.demoPanel { + .sampleDemoPanel(); +} + +.rootContainer { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 20px; + padding: 4px; +} + +.topRow { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + width: 100%; +} + +.nestedContainer { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 14px; + width: 100%; + padding: 16px; + box-sizing: border-box; + border: 1px dashed @border; + border-radius: 14px; + background-color: @surface-muted; +} + +.innerContainer { + display: flex; + flex-direction: column; + gap: 14px; +} + +.actionRow { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + width: 100%; +} + +.actionRowSecond { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + width: 100%; +} + +.cell { + display: flex; + min-width: 0; +} + +.cellButton { + width: 100%; +} + +.hidden { + display: none !important; +} + +@media (max-width: 900px) { + .topRow { + grid-template-columns: 1fr; + } +} + +@media (max-width: 720px) { + .actionRow, + .actionRowSecond { + grid-template-columns: 1fr; + } +} diff --git a/ui/spotlight-sandbox/src/views/HoldSample.js b/ui/spotlight-sandbox/src/views/HoldSample.js new file mode 100644 index 00000000..d54fbd21 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/HoldSample.js @@ -0,0 +1,96 @@ +import {useCallback, useEffect, useRef, useState} from 'react'; + +import HoldButton from '../components/HoldButton'; + +import css from './HoldSample.module.less'; + +const idleMessages = { + hold: 'waiting for hold events...', + pulse: 'waiting for pulse events...', + other: 'waiting for other events...' +}; + +const holdConfig = { + frequency: 200, + events: [ + {name: 'hold', time: 200}, + {name: 'longpress', time: 1000}, + {name: 'reallylongpress', time: 2000} + ] +}; + +const HoldSample = () => { + const [holdStatus, setHoldStatus] = useState(idleMessages.hold); + const [pulseStatus, setPulseStatus] = useState(idleMessages.pulse); + const [otherStatus, setOtherStatus] = useState(idleMessages.other); + const resetTimers = useRef({}); + + const report = useCallback((action, display) => { + const setters = { + hold: setHoldStatus, + pulse: setPulseStatus, + other: setOtherStatus + }; + + setters[display](`Select and Hold Me: ${action}`); + + clearTimeout(resetTimers.current[display]); + resetTimers.current[display] = setTimeout(() => { + setters[display](idleMessages[display]); + }, 2000); + }, []); + + useEffect(() => () => { + Object.values(resetTimers.current).forEach(clearTimeout); + }, []); + + const handleHeld = useCallback((ev) => { + report(ev.name, 'hold'); + }, [report]); + + const handlePulse = useCallback((ev) => { + report(`pulsing (${Math.round(ev.time)})`, 'pulse'); + }, [report]); + + const handleReleased = useCallback(() => { + report('released', 'hold'); + }, [report]); + + const handleTapped = useCallback(() => { + report('tapped', 'other'); + }, [report]); + + return ( +
+

+ Focus the button with arrow keys, then press and hold Enter to trigger hold events. + Quick press Enter to tap. Hold events are provided by @enact/ui/Touchable on a + Spottable control. +

+
+ + Select and Hold Me + +
    +
  • + {holdStatus} +
  • +
  • + {pulseStatus} +
  • +
  • + {otherStatus} +
  • +
+
+
+ ); +}; + +export default HoldSample; diff --git a/ui/spotlight-sandbox/src/views/HoldSample.module.less b/ui/spotlight-sandbox/src/views/HoldSample.module.less new file mode 100644 index 00000000..ca1e054e --- /dev/null +++ b/ui/spotlight-sandbox/src/views/HoldSample.module.less @@ -0,0 +1,37 @@ +@import '../styles/sampleTheme.less'; + +.holdSample { + .sampleRoot(); +} + +.description { + .sampleDescription(); +} + +.demoPanel { + .sampleDemoPanel(); + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 20px; +} + +.statusList { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 12px; + width: 100%; + margin: 0; + padding: 0; +} + +.statusItem { + .statusCard(); +} + +.statusItem em { + color: @text; + font-style: normal; + font-family: @mono; + font-size: 13px; +} diff --git a/ui/spotlight-sandbox/src/views/MainView.js b/ui/spotlight-sandbox/src/views/MainView.js new file mode 100644 index 00000000..25e6cb49 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/MainView.js @@ -0,0 +1,31 @@ +import BodyText from '@enact/ui/BodyText'; +import Heading from '@enact/ui/Heading'; +import {useNavigate} from 'react-router'; + +import SampleItem from '../components/SampleItem'; +import {sampleRoutes} from '../routes'; + +import css from './MainView.module.less'; + +const MainView = () => { + const navigate = useNavigate(); + + return ( +
+ Spotlight Sandbox + + Select a sample to explore standalone @enact/spotlight behavior. Each sample + runs on its own page so 5-way navigation stays predictable. + +
+ {sampleRoutes.map(({path, title}) => ( + + {title} + + ))} +
+
+ ); +}; + +export default MainView; diff --git a/ui/spotlight-sandbox/src/views/MainView.module.less b/ui/spotlight-sandbox/src/views/MainView.module.less new file mode 100644 index 00000000..be394ceb --- /dev/null +++ b/ui/spotlight-sandbox/src/views/MainView.module.less @@ -0,0 +1,28 @@ +@import '~@enact/ui/styles/mixins.less'; +@import '../styles/sampleTheme.less'; + +.main { + background: + radial-gradient(circle at top, rgba(99, 102, 241, 0.08), transparent 34%), + #f1f5f9; + position: absolute; + .position(0); + padding: 40px clamp(24px, 4vw, 56px); + overflow: auto; +} + +.intro { + max-width: 640px; + margin-bottom: 8px; + color: @text-muted; + font-size: 15px; + line-height: 1.6; +} + +.list { + display: flex; + flex-direction: column; + gap: 12px; + max-width: 560px; + margin-top: 28px; +} diff --git a/ui/spotlight-sandbox/src/views/SamplePage.js b/ui/spotlight-sandbox/src/views/SamplePage.js new file mode 100644 index 00000000..ef81cedf --- /dev/null +++ b/ui/spotlight-sandbox/src/views/SamplePage.js @@ -0,0 +1,33 @@ +import Heading from '@enact/ui/Heading'; +import PropTypes from 'prop-types'; +import {useCallback} from 'react'; +import {useNavigate} from 'react-router'; + +import SpottableButton from '../components/SpottableButton'; + +import css from './SamplePage.module.less'; + +const SamplePage = ({children, title}) => { + const navigate = useNavigate(); + + const handleBack = useCallback(() => { + navigate('/'); + }, [navigate]); + + return ( +
+ + Back to Samples + + {title} + {children} +
+ ); +}; + +SamplePage.propTypes = { + children: PropTypes.node, + title: PropTypes.string +}; + +export default SamplePage; diff --git a/ui/spotlight-sandbox/src/views/SamplePage.module.less b/ui/spotlight-sandbox/src/views/SamplePage.module.less new file mode 100644 index 00000000..8cef14ac --- /dev/null +++ b/ui/spotlight-sandbox/src/views/SamplePage.module.less @@ -0,0 +1,16 @@ +@import '~@enact/ui/styles/mixins.less'; +@import '../styles/sampleTheme.less'; + +.page { + background: + radial-gradient(circle at top, rgba(99, 102, 241, 0.08), transparent 34%), + #f1f5f9; + position: absolute; + .position(0); + padding: 40px clamp(24px, 4vw, 56px); + overflow: auto; +} + +.backButton { + margin-bottom: 20px; +} diff --git a/ui/spotlight-sandbox/src/views/SandboxSample.js b/ui/spotlight-sandbox/src/views/SandboxSample.js new file mode 100644 index 00000000..1c6c4998 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/SandboxSample.js @@ -0,0 +1,62 @@ +import {useCallback, useState} from 'react'; + +import Barracuda from '../components/Barracuda'; +import SpottableButton from '../components/SpottableButton'; + +import css from './SandboxSample.module.less'; + +const createInitialControls = () => { + const controls = []; + let id = 0; + + for (let row = 0; row < 2; row++) { + for (let column = 0; column < 4; column++) { + controls.push({ + id: `control-${id++}`, + left: 100 + column * 100, + top: 100 * (row + 1) + }); + } + } + + return controls; +}; + +const SandboxSample = () => { + const [controls, setControls] = useState(createInitialControls); + + const handleAddControl = useCallback(() => { + setControls((current) => current.concat({ + id: `control-${current.length}`, + left: 20 + (current.length % 8) * 70, + top: 280 + Math.floor(current.length / 8) * 70 + })); + }, []); + + return ( +
+

+ Draggable spottable controls arranged in a grid. Drag to reposition or use the + bottom-right corner to resize. Use arrow keys to navigate between controls after + repositioning them. +

+
+
+ Add Control +
+
+ {controls.map(({id, left, top}, index) => ( + + ))} +
+
+
+ ); +}; + +export default SandboxSample; diff --git a/ui/spotlight-sandbox/src/views/SandboxSample.module.less b/ui/spotlight-sandbox/src/views/SandboxSample.module.less new file mode 100644 index 00000000..045810d9 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/SandboxSample.module.less @@ -0,0 +1,24 @@ +@import '../styles/sampleTheme.less'; + +.sandboxSample { + .sampleRoot(); +} + +.description { + .sampleDescription(); +} + +.demoPanel { + .sampleDemoPanel(); +} + +.toolbar { + margin-bottom: 16px; +} + +.stage { + .sampleStage(); + min-height: 360px; + margin-top: 0; + overflow: auto; +} diff --git a/ui/spotlight-sandbox/src/views/TestPage.js b/ui/spotlight-sandbox/src/views/TestPage.js new file mode 100644 index 00000000..a9aa9ec6 --- /dev/null +++ b/ui/spotlight-sandbox/src/views/TestPage.js @@ -0,0 +1,38 @@ +import SpottableButton from '../components/SpottableButton'; + +import css from './TestPage.module.less'; + +const TestPage = () => ( +
+

+ Spatial navigation test layout with wide and tall controls. Use arrow keys to verify + Spotlight chooses the nearest neighbor across differently sized targets. +

+
+
+
+ + A + + + B + + + C + + + D + + + E + + + F + +
+
+
+
+); + +export default TestPage; diff --git a/ui/spotlight-sandbox/src/views/TestPage.module.less b/ui/spotlight-sandbox/src/views/TestPage.module.less new file mode 100644 index 00000000..cedae3fb --- /dev/null +++ b/ui/spotlight-sandbox/src/views/TestPage.module.less @@ -0,0 +1,61 @@ +@import '../styles/sampleTheme.less'; + +@test-canvas-width: 1080px; +@test-canvas-height: 1340px; + +.testPage { + .sampleRoot(); +} + +.description { + .sampleDescription(); +} + +.demoPanel { + .sampleDemoPanel(); + padding: 16px; +} + +.stage { + .sampleStage(); + width: 100%; + max-height: calc(100vh - 240px); + min-height: 360px; + overflow: auto; +} + +.canvas { + position: relative; + width: @test-canvas-width; + height: @test-canvas-height; +} + +.button, +.buttonA { + position: absolute; + margin: 0; + min-width: 0; + border-color: @container-active; + font-family: @mono; + font-weight: 700; +} + +.bigItem { + border-color: @test-wide; + background-color: fade(@test-wide, 8%); + + &[data-spotlight-focused] { + border-color: @test-wide; + box-shadow: 0 0 0 3px @test-wide-soft; + } +} + +.bigItemVertical { + border-color: @test-tall; + background-color: fade(@test-tall, 8%); + + &[data-spotlight-focused] { + border-color: @test-tall; + box-shadow: 0 0 0 3px @test-tall-soft; + } +} diff --git a/ui/spotlight-sandbox/webos-meta/appinfo.json b/ui/spotlight-sandbox/webos-meta/appinfo.json new file mode 100644 index 00000000..0549700c --- /dev/null +++ b/ui/spotlight-sandbox/webos-meta/appinfo.json @@ -0,0 +1,12 @@ +{ + "id": "com.enactjs.app.spotlight-sandbox", + "version": "1.0.0", + "vendor": "LGE-SVL", + "type": "web", + "main": "index.html", + "title": "Spotlight Sandbox", + "icon": "icon.png", + "miniicon": "icon-mini.png", + "largeIcon": "icon-large.png", + "uiRevision": 2 +} From 72a943dbe79a91ea0f2b47d64354765f73c278bd Mon Sep 17 00:00:00 2001 From: Dan Ichim Date: Tue, 30 Jun 2026 15:38:02 +0300 Subject: [PATCH 2/6] draggable control --- .../components/{Barracuda.js => DraggableControl.js} | 11 ++++++----- ...acuda.module.less => DraggableControl.module.less} | 2 +- ui/spotlight-sandbox/src/views/SandboxSample.js | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) rename ui/spotlight-sandbox/src/components/{Barracuda.js => DraggableControl.js} (91%) rename ui/spotlight-sandbox/src/components/{Barracuda.module.less => DraggableControl.module.less} (98%) diff --git a/ui/spotlight-sandbox/src/components/Barracuda.js b/ui/spotlight-sandbox/src/components/DraggableControl.js similarity index 91% rename from ui/spotlight-sandbox/src/components/Barracuda.js rename to ui/spotlight-sandbox/src/components/DraggableControl.js index 455d394a..178ffcef 100644 --- a/ui/spotlight-sandbox/src/components/Barracuda.js +++ b/ui/spotlight-sandbox/src/components/DraggableControl.js @@ -2,14 +2,15 @@ import Spottable from '@enact/spotlight/Spottable'; import Spotlight from '@enact/spotlight'; import PropTypes from 'prop-types'; import {useCallback, useRef, useState} from 'react'; -import css from './Barracuda.module.less'; + +import css from './DraggableControl.module.less'; const SpottableDiv = Spottable('div'); const CORNER_SIZE = 20; const DEFAULT_SIZE = 50; -const Barracuda = ({initialLeft = 0, initialTop = 0, label, ...rest}) => { +const DraggableControl = ({initialLeft = 0, initialTop = 0, label, ...rest}) => { const [position, setPosition] = useState({left: initialLeft, top: initialTop}); const [size, setSize] = useState({width: DEFAULT_SIZE, height: DEFAULT_SIZE}); const dragState = useRef(null); @@ -76,7 +77,7 @@ const Barracuda = ({initialLeft = 0, initialTop = 0, label, ...rest}) => { return ( { ); }; -Barracuda.propTypes = { +DraggableControl.propTypes = { initialLeft: PropTypes.number, initialTop: PropTypes.number, label: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }; -export default Barracuda; +export default DraggableControl; diff --git a/ui/spotlight-sandbox/src/components/Barracuda.module.less b/ui/spotlight-sandbox/src/components/DraggableControl.module.less similarity index 98% rename from ui/spotlight-sandbox/src/components/Barracuda.module.less rename to ui/spotlight-sandbox/src/components/DraggableControl.module.less index f2d57a31..94a925e2 100644 --- a/ui/spotlight-sandbox/src/components/Barracuda.module.less +++ b/ui/spotlight-sandbox/src/components/DraggableControl.module.less @@ -1,6 +1,6 @@ @import '../styles/sampleTheme.less'; -.barracuda { +.control { position: absolute; display: flex; align-items: center; diff --git a/ui/spotlight-sandbox/src/views/SandboxSample.js b/ui/spotlight-sandbox/src/views/SandboxSample.js index 1c6c4998..859b30b7 100644 --- a/ui/spotlight-sandbox/src/views/SandboxSample.js +++ b/ui/spotlight-sandbox/src/views/SandboxSample.js @@ -1,6 +1,6 @@ import {useCallback, useState} from 'react'; -import Barracuda from '../components/Barracuda'; +import DraggableControl from '../components/DraggableControl'; import SpottableButton from '../components/SpottableButton'; import css from './SandboxSample.module.less'; @@ -46,7 +46,7 @@ const SandboxSample = () => {
{controls.map(({id, left, top}, index) => ( - Date: Tue, 30 Jun 2026 16:19:09 +0300 Subject: [PATCH 3/6] add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dd159ef..5d07aa4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ The following is a curated list of changes in the Enact samples module, newest c ## [unreleased] +- Added `ui/spotlight-sandbox` sample demonstrating standalone `@enact/spotlight` usage - Updated minor dependencies ## [2.7.0] - 2023-05-09 From 14e7cefed90a7c7159555ac971da1ba1e7728c98 Mon Sep 17 00:00:00 2001 From: Dan Ichim Date: Fri, 3 Jul 2026 11:16:29 +0300 Subject: [PATCH 4/6] add ramda --- ui/spotlight-sandbox/package-lock.json | 1 + ui/spotlight-sandbox/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/spotlight-sandbox/package-lock.json b/ui/spotlight-sandbox/package-lock.json index 532390f8..85045f56 100644 --- a/ui/spotlight-sandbox/package-lock.json +++ b/ui/spotlight-sandbox/package-lock.json @@ -15,6 +15,7 @@ "@enact/ui": "^5.4.2", "ilib": "^14.21.2", "prop-types": "^15.8.1", + "ramda": "^0.32.0", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router": "^7.13.1" diff --git a/ui/spotlight-sandbox/package.json b/ui/spotlight-sandbox/package.json index a7bcff37..76df4633 100644 --- a/ui/spotlight-sandbox/package.json +++ b/ui/spotlight-sandbox/package.json @@ -32,6 +32,7 @@ "@enact/ui": "^5.4.2", "ilib": "^14.21.2", "prop-types": "^15.8.1", + "ramda": "^0.32.0", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router": "^7.13.1" From a4822079b1457465f61bef4d5bf8920b9ce31e76 Mon Sep 17 00:00:00 2001 From: Dan Ichim Date: Fri, 3 Jul 2026 11:22:10 +0300 Subject: [PATCH 5/6] add icons --- ui/spotlight-sandbox/webos-meta/icon-large.png | Bin 0 -> 4151 bytes ui/spotlight-sandbox/webos-meta/icon-mini.png | Bin 0 -> 2004 bytes ui/spotlight-sandbox/webos-meta/icon.png | Bin 0 -> 2562 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ui/spotlight-sandbox/webos-meta/icon-large.png create mode 100644 ui/spotlight-sandbox/webos-meta/icon-mini.png create mode 100644 ui/spotlight-sandbox/webos-meta/icon.png diff --git a/ui/spotlight-sandbox/webos-meta/icon-large.png b/ui/spotlight-sandbox/webos-meta/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..d237e9fb1ec8d3d721bdc64ddc52ed7419ae43c3 GIT binary patch literal 4151 zcmb7HS5y+HS`_?q<0WP=pa>klSuDDq$3iF(vgl7W9UUdlqL!Y zC`gc|fC}>B=lggc@9f!~vpYLycXnoW?xdO+>w##%GynhqgwWSEBhrL_or;t=Qn)17 z5ed16hLHvU(3(wq;X*;2LtOREi~xX00RSNWApr23xD>ww0Nj@X0KPc`07``b0DC}b zucUNYgmM@?3XTwU0;le zAB01?B|i_ulb;!_>a4tAgLfj~xvq+}42m&cPge2rr zJkmCMGz2224xnHUAUgvUs;-h?4FE>D98-{yvyuHbQT9E(ELeXC z{g7xV-bbgU)_L{()x+e-mR#WfSA-j7`aU_OME(a1Szb{XAFYrnrPryyxv zx~FI@jNON{4xG8G3xnxn8rqk&2^uZ|9l8%N}15|3j%KyKCX7ly?I*vc3}z8R1jA*=teLZReISRl510C59uyEGfA z$k$BM?O+gEyiB1#)uh22by~Y0V(BBz7IRK^<-cKNY05&=<|%^3jqr1mQ$3xZaLVvh z*iF1Jj9tOOW$BFP_E7g&;{YUSnj8Ff`>>@kLkeCobtb}1Xio|rJBb~}08``p=v41L zZA&9z?>El6faTAfr^V|#8Lu-)ZEo-KHSyDD->3FO-oaBZdtG!Vp3?N$R|zDf^Z_j0 zmPg~$a}`4w7&V)z)RJWNrOlS$uWzQHG<5-_YZ8*RoTW%t$`EEqY6M7IiKm7tlp%0x z{s8HvwdOlR9Y+u5O=RTyQ)E-)kMWWmjpbHLbh_0&ilOgWbsE#Y`8nbYdP#Y4O3lC> z{m!ty?9q^rqcsy3*M>utKjxP#DTyl(M-B90tf(`TknWn;HdFPktl_BFnGvPAh^?{S zEkOmPVqMV``i4jM7-{rk%Z-SVZD}_hYhZ%Mj<@iw)ga(%#y764e({=Fh$%VzPe`^D zY%(*lX)(8isp#>Y*RAaHeT7JsXa)CodpK<{mHYBS=Xs3U%<9FCbeUz9;N53NIxWHR zcx+o?YxE!hf`XU_q6Y6(~6`KR#u! z6kyntQ}Ff3Nqn*-8vX-c(js6xj_~lX-0QG5CI1V+o)|`WwFcK0O+-Ix+aJx@Sh@gT z)+V{Wuo9C;XBpTLP&F?$)Q98ijM1`N?uT7!Up_I$NOYcQy<3}1{42<;pxkGVr_5w= zFFRcmiQ~h!7RdM}GmMgocp)4ls@v>tn^Ri!zff9BKVn|M`2Ias+RtC*M*2FHXbhaf zHCuWn7bU|L@zcPjg#cl33!j92m87rXU)=elSs&CPJ4|fH;P01CTFGf;W4i; z2G2z*)`q+AQ%y-bEWftj0tX{r$N$q@bn&&y?(RC(SJKNSwM5s4k)W}yP-|I`9Hzk? zVP2w%=Ht&T0l`*wI5!`lf0i*0p9OtCuyb!JQN7+({9Fja_^RJ=bJ1_-vnxZj^ba zwaFILybb{abWCht-HlTprCXze_OM*!L~JgG((l~&p0iaTVM~0g{HgJ2oyi9|q#R|T zIhqgpK-BUb%luwd)I+zMjKBA}ui45J6(IEZr`@XfODy7t`oMfe!qIOHf;m6lQZ91# zxiTyUY8T*Ei^eVfc+u} zgz1aXfaE4lI0J(Hg@&rEVr?--)}WvKeDatTWv&((&vK)vxu!% zd;YwYhE-jvoJ46Z$2?>l@zq$+Z<}bs02+&Lej%3TA>$c&b8U=y|8&lsx!ZpyiekW# z@N0$sjZKI;!8?afU>4~Sk>?smPEK`e!HORmS8i72M5ej^5w#LkOl|po(%d&YDFs5F zJen~*w>R`M!Vc!-m=S-JIJSnyfluj=_G9nRXsB(;_LnFJNrAmH9V_cl)o(40^!Q`) z6xfj4kS8*21Ji18qaBe=3);>42;Rk!WcP^AsQaS6vG0&iYf`TNWbwdK^?$*Q#0iQ2 zK3`1Em=1hN78MI~!jM|ntgh#hCF@O^4yM*&@{FxPjiDH3Q%mj>?1+!a`qG`jk|Nf7 z9RZD5?R?ylg5-aa`wBO9$@f0EP--M3{=U}q-uzK@jGlGIA^>9I?3NxEH?^eP1%D=% za?ukI#5mP_?8)_&_wOMZBAHj;;UMw;ZLsMpE-*@-nv|scj!f4?veBw1WE071&@4L; z_G+CaCht5HPoFSEOnnaZx70-Ia1}%sWWlY-4aQ6qCeJ866Yt&OGm!9~YB(Hw;6oh; z@l9)or2E{5EwvL&V9)^vvBMF0w^7iH^xz0(X7p_M-xpClOUoXwj0Q!0TQdxEWolJ7d-q>UV1j=A+mGI_X=az;u1`guz#p*_Tj6p+iQGia{~86RaXm3gi&RG!W(aK4OBOoVWkEp@6Fx5+vv=zh>;=Wj!SF* zjJ#x#%~mK}JoK6vW5dkhpFjzsv1Y&`Pt_8)T5T8io{P=WcS%$K_Eb1vnXjC6(6yB~ zOWNhccU_OP=`Q$Hd(dr%7i>gT^N{b^D;KSA)BQ;%dPStPbzQfa&TpMGH{?!BlIvCC zUuDsJ$zIR)qqUiUa81J?7ThPEHF}wzp+@)4)9@YVarRI~8x|3oh~}(g7|cLNL86Fe#nBH0{;1`4Hi114vkA9Uzb-RM-2rW9v{gv$n*D zc#c>(UX3Uh?p{Xv8W)hb?Dn06<@%YvI#tC66KeFA%NNi&XWieV(B``Rhz#jN=f;@e%9$eHQ~x?E^-v>5(|xo@&GtJTGex{azFF zmWX3=rz*3{&~4=%VZxaunSF7Jq<=WRE}pM_rJD|5nH5#3P6sBCg+=7GMl`Uc1;4o> zq2LoRu~om%1gC89O>$z#jLCijUVqX)=ZueR)d(PX1Qa*lZE2y8ywTcRkd!C53`b4j zSLFB=*$w!pG(y4CZm8Gp_U?~2i(NhPQ?9jEFPcSvdqd8T zR9+_dq&8R_OG`e|Z?SteR@?OO@>ZW}78j~&gJd;@b~#a%J~7yXnuPll6?h!H9CQ}Y8=qVqw@ zsPO=|yPs%eBF*Q5!89i!;msv0_>(Z?Ou9O{{Qlqbgnq40jXaVkm8PwXV)tfcCeAf? zSZyy;>VT2i znVe}dDdn!-qrpwEG3auZG1yD68D-4Kk6E?}GKVkx>dRX=rXBv^(c=Jl)t&qYD|WAN z8ztJtvOtR?h+G|W5AL}J-x;kZ5av}T4*tke3gFJ?0up$aRMr`0iflKG1@kGGz6+3? z_=IGyJ7vAK#e0krvCd;8-SmsC%}}_tk0jXt2`nmP>QIb<3)gFjeey$gs#>Fx%1Z9n zPpz81)$degmuf`N(2Cch)RKwh{u38C^^By4Y;@+*^o!N6%Jh-9Ct_4IRPj-%-RRMzp1}VAGOH(Vk>1%A!lF$kb zUhAiW1eeIfJN9?rJ_oIN9`{Z`c2)43Q0xO|<1ZxzQ%wQ2qC*SG%dAAh9D{rG!&H$7 zGTi$vD8ppfFU0l5JyUWTE-oUd_;j>xd;@!M;+F~qUDIh9m>&Eoy}!L_wGh^=HgVV> z%(nYR&yeWjBC%VjiQDR+7;*a*z1m0tbL}8=?T`zZ1;;-9y4$g4_TaD1W3I1mWl6;b!LMf{F+ncl%e(0}wjK K+D)3y3I73^C56KP literal 0 HcmV?d00001 diff --git a/ui/spotlight-sandbox/webos-meta/icon-mini.png b/ui/spotlight-sandbox/webos-meta/icon-mini.png new file mode 100644 index 0000000000000000000000000000000000000000..9771fac4c525609c12f7ecea8f7c429724ae69ec GIT binary patch literal 2004 zcmV;_2P^oAP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 Lv^1#EbBp~q)m*Mh literal 0 HcmV?d00001 From be987e1735814d683c96ace92c065a95768a79c0 Mon Sep 17 00:00:00 2001 From: Dan Ichim Date: Fri, 3 Jul 2026 12:50:19 +0300 Subject: [PATCH 6/6] change sizes --- ui/spotlight-sandbox/src/App/App.module.less | 17 +++++ .../src/components/DraggableControl.js | 4 +- .../components/DraggableControl.module.less | 12 ++-- .../SampleItem/SampleItem.module.less | 16 ++--- .../components/SpottableButton.module.less | 14 ++-- .../src/styles/sampleTheme.less | 71 ++++++++++++------- .../src/views/ContainerSample.module.less | 12 ++-- .../src/views/DisappearSample.module.less | 18 ++--- .../src/views/HoldSample.module.less | 8 +-- .../src/views/MainView.module.less | 14 ++-- .../src/views/SamplePage.module.less | 4 +- .../src/views/SandboxSample.js | 8 +-- .../src/views/SandboxSample.module.less | 4 +- ui/spotlight-sandbox/src/views/TestPage.js | 12 ++-- .../src/views/TestPage.module.less | 10 +-- 15 files changed, 129 insertions(+), 95 deletions(-) diff --git a/ui/spotlight-sandbox/src/App/App.module.less b/ui/spotlight-sandbox/src/App/App.module.less index 59400c26..fe4dabf0 100644 --- a/ui/spotlight-sandbox/src/App/App.module.less +++ b/ui/spotlight-sandbox/src/App/App.module.less @@ -1,3 +1,5 @@ +@import '../styles/sampleTheme.less'; + :global { html, body, @@ -5,6 +7,21 @@ height: 100%; margin: 0; overflow: hidden; + .sampleTypography(); + } + + h1 { + .sampleTitle(); + } + + p, + ul { + margin: 0; + } + + ul { + padding: 0; + list-style: none; } } diff --git a/ui/spotlight-sandbox/src/components/DraggableControl.js b/ui/spotlight-sandbox/src/components/DraggableControl.js index 178ffcef..a2c4d772 100644 --- a/ui/spotlight-sandbox/src/components/DraggableControl.js +++ b/ui/spotlight-sandbox/src/components/DraggableControl.js @@ -7,8 +7,8 @@ import css from './DraggableControl.module.less'; const SpottableDiv = Spottable('div'); -const CORNER_SIZE = 20; -const DEFAULT_SIZE = 50; +const CORNER_SIZE = 27; +const DEFAULT_SIZE = 69; const DraggableControl = ({initialLeft = 0, initialTop = 0, label, ...rest}) => { const [position, setPosition] = useState({left: initialLeft, top: initialTop}); diff --git a/ui/spotlight-sandbox/src/components/DraggableControl.module.less b/ui/spotlight-sandbox/src/components/DraggableControl.module.less index 94a925e2..b3b3e2ce 100644 --- a/ui/spotlight-sandbox/src/components/DraggableControl.module.less +++ b/ui/spotlight-sandbox/src/components/DraggableControl.module.less @@ -6,16 +6,16 @@ align-items: center; justify-content: center; border: 1px solid #10b981; - border-radius: 10px; + border-radius: 15px; background: linear-gradient(180deg, rgba(16, 185, 129, 0.18) 0%, rgba(16, 185, 129, 0.08) 100%); color: @text; font-family: @mono; - font-size: 12px; + font-size: 18px; font-weight: 700; user-select: none; touch-action: none; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); + box-shadow: 0 3px 3px rgba(15, 23, 42, 0.08); &[data-spotlight-focused] { border-color: @accent; @@ -27,10 +27,10 @@ position: absolute; right: 0; bottom: 0; - width: 18px; - height: 18px; + width: 27px; + height: 27px; border: 1px solid @border; - border-radius: 8px 0 10px 0; + border-radius: 12px 0 15px 0; background-color: @surface-muted; cursor: nwse-resize; } diff --git a/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less index 5857330c..7fd4aacc 100644 --- a/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less +++ b/ui/spotlight-sandbox/src/components/SampleItem/SampleItem.module.less @@ -5,13 +5,13 @@ align-items: center; justify-content: space-between; width: 100%; - min-height: 56px; - padding: 14px 18px; + min-height: 78px; + padding: 21px 27px; border: 1px solid @border; - border-radius: 14px; + border-radius: 21px; background-color: @surface; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); - font-size: 15px; + box-shadow: 0 3px 3px rgba(15, 23, 42, 0.04); + font-size: 21px; font-weight: 600; text-align: left; transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; @@ -19,18 +19,18 @@ &::after { content: '→'; color: @text-muted; - font-size: 18px; + font-size: 24px; transition: transform 160ms ease, color 160ms ease; } &[data-spotlight-focused] { border-color: @accent; box-shadow: 0 0 0 3px @accent-soft; - transform: translateY(-1px); + transform: translateY(-3px); &::after { color: @accent; - transform: translateX(4px); + transform: translateX(6px); } } } diff --git a/ui/spotlight-sandbox/src/components/SpottableButton.module.less b/ui/spotlight-sandbox/src/components/SpottableButton.module.less index 7f8663ec..c58d0987 100644 --- a/ui/spotlight-sandbox/src/components/SpottableButton.module.less +++ b/ui/spotlight-sandbox/src/components/SpottableButton.module.less @@ -5,28 +5,28 @@ align-items: center; justify-content: center; box-sizing: border-box; - min-width: 88px; - min-height: 44px; + min-width: 123px; + min-height: 63px; height: auto; - padding: 10px 16px; + padding: 15px 24px; margin: 0; border: 1px solid @border; - border-radius: 10px; + border-radius: 15px; background-color: @surface; color: @text; - font-size: 14px; + font-size: 21px; font-weight: 500; line-height: 1.3; text-align: center; cursor: pointer; white-space: normal; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + box-shadow: 0 3px 3px rgba(15, 23, 42, 0.04); transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; &[data-spotlight-focused] { border-color: @accent; box-shadow: 0 0 0 3px @accent-soft; - transform: translateY(-1px); + transform: translateY(-3px); } &[aria-disabled='true'] { diff --git a/ui/spotlight-sandbox/src/styles/sampleTheme.less b/ui/spotlight-sandbox/src/styles/sampleTheme.less index 9d001da9..91fe2853 100644 --- a/ui/spotlight-sandbox/src/styles/sampleTheme.less +++ b/ui/spotlight-sandbox/src/styles/sampleTheme.less @@ -11,24 +11,43 @@ @test-wide-soft: rgba(14, 165, 233, 0.14); @test-tall: #8b5cf6; @test-tall-soft: rgba(139, 92, 246, 0.14); -@shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 8px 24px rgba(15, 23, 42, 0.06); +@shadow: 0 3px 3px rgba(15, 23, 42, 0.06), 0 12px 33px rgba(15, 23, 42, 0.06); @mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; +@font-base: 21px; +@font-title: 45px; +@font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + +.sampleTypography() { + font-family: @font-sans; + font-size: @font-base; + line-height: 1.6; + color: @text; +} + +.sampleTitle() { + margin: 0 0 18px; + font-family: @font-sans; + font-size: @font-title; + font-weight: 700; + line-height: 1.2; + color: @text; +} .sampleRoot() { - max-width: 960px; + max-width: 1344px; } .sampleDescription() { - margin: 0 0 20px; - font-size: 15px; + margin: 0 0 30px; + font-size: @font-base; line-height: 1.6; color: @text-muted; } .sampleDemoPanel() { - padding: 24px; + padding: 36px; border: 1px solid @border; - border-radius: 16px; + border-radius: 24px; background: radial-gradient(circle at top left, rgba(99, 102, 241, 0.08), transparent 42%), linear-gradient(180deg, @surface-muted 0%, @surface 100%); @@ -37,18 +56,18 @@ .sampleStage() { position: relative; - min-height: 360px; + min-height: 504px; border: 1px solid @border; - border-radius: 14px; + border-radius: 21px; background-color: @surface; - box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.04); + box-shadow: inset 0 3px 3px rgba(15, 23, 42, 0.04); } .spotlightContainer() { border: 1px solid @border; - border-radius: 14px; + border-radius: 21px; background-color: @surface; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + box-shadow: 0 3px 3px rgba(15, 23, 42, 0.04); transition: border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease; &:focus-within { @@ -56,19 +75,19 @@ background-color: #fff7f8; box-shadow: 0 0 0 3px @container-active-soft, - 0 10px 24px rgba(244, 63, 94, 0.08); + 0 15px 33px rgba(244, 63, 94, 0.08); } } .monoBadge() { display: inline-flex; align-self: flex-start; - padding: 4px 10px; + padding: 6px 15px; border-radius: 999px; background-color: @accent-soft; color: @accent; font-family: @mono; - font-size: 12px; + font-size: 18px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; @@ -77,17 +96,17 @@ .statusBar() { display: inline-flex; align-items: center; - gap: 10px; - margin-top: 20px; - padding: 10px 14px; + gap: 15px; + margin-top: 30px; + padding: 15px 21px; border: 1px solid @border; - border-radius: 12px; + border-radius: 18px; background-color: @surface; box-shadow: @shadow; } .statusLabel() { - font-size: 13px; + font-size: 18px; font-weight: 600; color: @text-muted; text-transform: uppercase; @@ -95,23 +114,23 @@ } .statusValue() { - padding: 4px 10px; - border-radius: 8px; + padding: 6px 15px; + border-radius: 12px; background-color: @surface-muted; color: @text; font-family: @mono; - font-size: 14px; + font-size: 21px; } .statusCard() { margin: 0; - padding: 12px 14px; + padding: 18px 21px; border: 1px solid @border; - border-radius: 12px; + border-radius: 18px; background-color: @surface; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + box-shadow: 0 3px 3px rgba(15, 23, 42, 0.04); color: @text-muted; - font-size: 14px; + font-size: 21px; line-height: 1.5; list-style: none; } diff --git a/ui/spotlight-sandbox/src/views/ContainerSample.module.less b/ui/spotlight-sandbox/src/views/ContainerSample.module.less index 997e1d8d..7eb71424 100644 --- a/ui/spotlight-sandbox/src/views/ContainerSample.module.less +++ b/ui/spotlight-sandbox/src/views/ContainerSample.module.less @@ -14,8 +14,8 @@ .layout { display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(393px, 1fr)); + gap: 30px; align-items: start; } @@ -23,15 +23,15 @@ position: relative; display: flex; flex-direction: column; - gap: 12px; + gap: 18px; min-width: 0; - padding: 16px; + padding: 24px; .spotlightContainer(); } .nested { flex: 1 1 100%; - margin-top: 4px; + margin-top: 6px; background-color: @surface-muted; } @@ -42,7 +42,7 @@ .controls { display: flex; flex-wrap: wrap; - gap: 10px; + gap: 15px; } .statusBar { diff --git a/ui/spotlight-sandbox/src/views/DisappearSample.module.less b/ui/spotlight-sandbox/src/views/DisappearSample.module.less index 4b6abf58..177e599f 100644 --- a/ui/spotlight-sandbox/src/views/DisappearSample.module.less +++ b/ui/spotlight-sandbox/src/views/DisappearSample.module.less @@ -16,14 +16,14 @@ display: flex; flex-direction: column; align-items: stretch; - gap: 20px; - padding: 4px; + gap: 30px; + padding: 6px; } .topRow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; + gap: 18px; width: 100%; } @@ -31,32 +31,32 @@ display: flex; flex-direction: column; align-items: stretch; - gap: 14px; + gap: 21px; width: 100%; - padding: 16px; + padding: 24px; box-sizing: border-box; border: 1px dashed @border; - border-radius: 14px; + border-radius: 21px; background-color: @surface-muted; } .innerContainer { display: flex; flex-direction: column; - gap: 14px; + gap: 21px; } .actionRow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; + gap: 18px; width: 100%; } .actionRowSecond { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; + gap: 18px; width: 100%; } diff --git a/ui/spotlight-sandbox/src/views/HoldSample.module.less b/ui/spotlight-sandbox/src/views/HoldSample.module.less index ca1e054e..d06556b1 100644 --- a/ui/spotlight-sandbox/src/views/HoldSample.module.less +++ b/ui/spotlight-sandbox/src/views/HoldSample.module.less @@ -13,13 +13,13 @@ display: flex; flex-direction: column; align-items: flex-start; - gap: 20px; + gap: 30px; } .statusList { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 12px; + grid-template-columns: repeat(auto-fit, minmax(309px, 1fr)); + gap: 18px; width: 100%; margin: 0; padding: 0; @@ -33,5 +33,5 @@ color: @text; font-style: normal; font-family: @mono; - font-size: 13px; + font-size: 18px; } diff --git a/ui/spotlight-sandbox/src/views/MainView.module.less b/ui/spotlight-sandbox/src/views/MainView.module.less index be394ceb..2d3d4701 100644 --- a/ui/spotlight-sandbox/src/views/MainView.module.less +++ b/ui/spotlight-sandbox/src/views/MainView.module.less @@ -7,22 +7,20 @@ #f1f5f9; position: absolute; .position(0); - padding: 40px clamp(24px, 4vw, 56px); + padding: 57px clamp(33px, 4vw, 78px); overflow: auto; } .intro { - max-width: 640px; - margin-bottom: 8px; + max-width: 897px; + margin-block: 0 18px; color: @text-muted; - font-size: 15px; - line-height: 1.6; } .list { display: flex; flex-direction: column; - gap: 12px; - max-width: 560px; - margin-top: 28px; + gap: 18px; + max-width: 783px; + margin-top: 39px; } diff --git a/ui/spotlight-sandbox/src/views/SamplePage.module.less b/ui/spotlight-sandbox/src/views/SamplePage.module.less index 8cef14ac..5e3b793e 100644 --- a/ui/spotlight-sandbox/src/views/SamplePage.module.less +++ b/ui/spotlight-sandbox/src/views/SamplePage.module.less @@ -7,10 +7,10 @@ #f1f5f9; position: absolute; .position(0); - padding: 40px clamp(24px, 4vw, 56px); + padding: 57px clamp(33px, 4vw, 78px); overflow: auto; } .backButton { - margin-bottom: 20px; + margin-bottom: 30px; } diff --git a/ui/spotlight-sandbox/src/views/SandboxSample.js b/ui/spotlight-sandbox/src/views/SandboxSample.js index 859b30b7..355d826b 100644 --- a/ui/spotlight-sandbox/src/views/SandboxSample.js +++ b/ui/spotlight-sandbox/src/views/SandboxSample.js @@ -13,8 +13,8 @@ const createInitialControls = () => { for (let column = 0; column < 4; column++) { controls.push({ id: `control-${id++}`, - left: 100 + column * 100, - top: 100 * (row + 1) + left: 141 + column * 141, + top: 141 * (row + 1) }); } } @@ -28,8 +28,8 @@ const SandboxSample = () => { const handleAddControl = useCallback(() => { setControls((current) => current.concat({ id: `control-${current.length}`, - left: 20 + (current.length % 8) * 70, - top: 280 + Math.floor(current.length / 8) * 70 + left: 30 + (current.length % 8) * 99, + top: 393 + Math.floor(current.length / 8) * 99 })); }, []); diff --git a/ui/spotlight-sandbox/src/views/SandboxSample.module.less b/ui/spotlight-sandbox/src/views/SandboxSample.module.less index 045810d9..8c2871c9 100644 --- a/ui/spotlight-sandbox/src/views/SandboxSample.module.less +++ b/ui/spotlight-sandbox/src/views/SandboxSample.module.less @@ -13,12 +13,12 @@ } .toolbar { - margin-bottom: 16px; + margin-bottom: 24px; } .stage { .sampleStage(); - min-height: 360px; + min-height: 504px; margin-top: 0; overflow: auto; } diff --git a/ui/spotlight-sandbox/src/views/TestPage.js b/ui/spotlight-sandbox/src/views/TestPage.js index a9aa9ec6..df3a5671 100644 --- a/ui/spotlight-sandbox/src/views/TestPage.js +++ b/ui/spotlight-sandbox/src/views/TestPage.js @@ -11,22 +11,22 @@ const TestPage = () => (
- + A - + B - + C - + D - + E - + F
diff --git a/ui/spotlight-sandbox/src/views/TestPage.module.less b/ui/spotlight-sandbox/src/views/TestPage.module.less index cedae3fb..210abdbb 100644 --- a/ui/spotlight-sandbox/src/views/TestPage.module.less +++ b/ui/spotlight-sandbox/src/views/TestPage.module.less @@ -1,7 +1,7 @@ @import '../styles/sampleTheme.less'; -@test-canvas-width: 1080px; -@test-canvas-height: 1340px; +@test-canvas-width: 1512px; +@test-canvas-height: 1875px; .testPage { .sampleRoot(); @@ -13,14 +13,14 @@ .demoPanel { .sampleDemoPanel(); - padding: 16px; + padding: 24px; } .stage { .sampleStage(); width: 100%; - max-height: calc(100vh - 240px); - min-height: 360px; + max-height: calc(100vh - 336px); + min-height: 504px; overflow: auto; }