{"id":13715019,"url":"https://github.com/gohypergiant/FocusEngine","last_synced_at":"2025-05-07T03:31:07.545Z","repository":{"id":56824526,"uuid":"82228340","full_name":"gohypergiant/FocusEngine","owner":"gohypergiant","description":"A Framer module for simulating the grid focus behavior found on Apple TV and Roku.","archived":false,"fork":false,"pushed_at":"2017-10-18T15:30:54.000Z","size":866,"stargazers_count":23,"open_issues_count":2,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-18T23:45:34.630Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gohypergiant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-16T21:33:17.000Z","updated_at":"2020-11-10T16:54:52.000Z","dependencies_parsed_at":"2022-09-01T09:20:41.096Z","dependency_job_id":null,"html_url":"https://github.com/gohypergiant/FocusEngine","commit_stats":null,"previous_names":["bpxl-labs/focusengine"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gohypergiant%2FFocusEngine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gohypergiant%2FFocusEngine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gohypergiant%2FFocusEngine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gohypergiant%2FFocusEngine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gohypergiant","download_url":"https://codeload.github.com/gohypergiant/FocusEngine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252782577,"owners_count":21803404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T00:00:53.086Z","updated_at":"2025-05-07T03:31:06.681Z","avatar_url":"https://github.com/gohypergiant.png","language":"CoffeeScript","funding_links":[],"categories":["Modules","HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"# FocusEngine Framer Module\n\n[![license](https://img.shields.io/github/license/bpxl-labs/RemoteLayer.svg)](https://opensource.org/licenses/MIT)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](.github/CONTRIBUTING.md)\n[![Maintenance](https://img.shields.io/maintenance/yes/2017.svg)]()\n\n\u003ca href=\"https://open.framermodules.com/focusengine\"\u003e\u003cimg alt=\"Install with Framer Modules\" src=\"https://www.framermodules.com/assets/badge@2x.png\" width='160' height='40' /\u003e\u003c/a\u003e\n\nThe FocusEngine module allows you to simulate the grid focus behavior seen on streaming media players like Apple TV and Roku. Use the keyboard, [RemoteLayer](https://github.com/bpxl-labs/RemoteLayer), or another mechanism to direct focus around your prototype’s canvas.\n\nOnce initialized, any _visible_ layer can be brought into focus, even if it’s off screen. This permits the activation of off-screen menus. Visual appearance of focused elements can be customized.\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/935/24167436/bfaae7e0-0e44-11e7-846f-a47702716a28.gif\" width=\"497\" style=\"display: block; margin: auto\" alt=\"FocusEngine preview\" /\u003e\n\n### Installation\n\n#### NPM Installation\n\n```\n$ cd /your/framer/project\n$ npm i @blackpixel/framer-focusengine\n```\n\n#### Manual installation\n\nCopy or save the `FocusEngine.coffee` file into your project's `modules` folder.\n\n### Adding It to Your Project\n\nIn your Framer project add the following:\n\n```coffeescript\n# If you manually installed\nfe = require \"FocusEngine\"\n# else\nfe = require \"@blackpixel/framer-focusengine\"\n```\n\n\n### API\n\n#### Customize focused and unfocused states\n```coffeescript\nfe.focusStyle.scale = \u003cnumber\u003e\nfe.focusStyle.shadowX = \u003cnumber\u003e\nfe.focusStyle.shadowY = \u003cnumber\u003e\nfe.focusStyle.shadowColor = \u003cstring\u003e (hex or rgba)\nfe.focusStyle.shadowBlur = \u003cnumber\u003e\nfe.focusStyle.shadowSpread = \u003cnumber\u003e\n\nfe.unfocusStyle.shadowX = \u003cnumber\u003e\nfe.unfocusStyle.shadowY = \u003cnumber\u003e\nfe.unfocusStyle.shadowColor = \u003cstring\u003e (hex or rgba)\nfe.unfocusStyle.shadowBlur = \u003cnumber\u003e\nfe.unfocusStyle.shadowSpread = \u003cnumber\u003e\n```\n\n(Unfocused scale is always assumed to be the layer’s original scale. This need not be 1. You may get better visual results by drawing your layer slightly larger than needed and setting its initial scale to something less than 1.)\n\n#### Customize state switch duration\n\n```coffeescript\nfe.time = \u003cnumber\u003e\n```\n\n#### Collect layers that will participate into an array\n\t\n```coffeescript\nmyFocusableLayers = [layerA, layerB, layerC]\n```\n\n#### Initialize the engine with your array\n```coffeescript\nfe.initialize(myFocusableLayers)\n```\n\n#### Add a layer created post-initialization\n```coffeescript\nfe.addLayer(layerD)\n```\n\n#### Optionally attach changeFocus() to keyboard events\n```coffeescript\ndocument.addEventListener \"keydown\", (event) -\u003e\n\tkeyCode = event.which\n\tswitch keyCode\n\t\twhen 13 then fe.changeFocus(\"select\")\n\t\twhen 37 then fe.changeFocus(\"left\")\n\t\twhen 38 then fe.changeFocus(\"up\")\n\t\twhen 39 then fe.changeFocus(\"right\")\n\t\twhen 40 then fe.changeFocus(\"down\")\n\t\telse null\n```\n\n#### Place initial focus\n```coffeescript\nfe.placeFocus(layerA)\n```\n\n#### focusPrevious() is available to use in conjunction with FlowComponent's showPrevious()\n```coffeescript\nfe.focusPrevious()\n```\n\n(Note that focus cannot be placed on a layer whose visibility, or whose ancestor’s visibility, is false. You may need to delay calling `focusPrevious()` until the FlowComponent’s transition is ended, perhaps by using `.onTransitionEnd`.)\n\n#### Layers can trigger behavior upon receiving or losing focus, or being selected\n```coffeescript\nlayerA.on \"focus\", -\u003e\nlayerA.on \"unfocus\", -\u003e\nlayerA.on \"selected\", -\u003e\n```\n\nShortcuts are also available:\n\n```coffeescript\nlayerA.onFocus -\u003e\nlayerA.onUnfocus -\u003e\nlayerA.onSelected -\u003e\n```\n\n#### Integration with [RemoteLayer](https://github.com/bpxl-labs/RemoteLayer)\n```coffeescript\nRemoteLayer = require \"RemoteLayer\"\nmyRemote = new RemoteLayer\n\tclickAction: -\u003e fe.changeFocus(\"select\")\n\tswipeUpAction: -\u003e fe.changeFocus(\"up\")\n\tswipeDownAction: -\u003e fe.changeFocus(\"down\")\n\tswipeLeftAction: -\u003e fe.changeFocus(\"left\")\n\tswipeRightAction: -\u003e fe.changeFocus(\"right\")\n```\n\n#### Check the currently focused layer\n```coffeescript\nprint fe.focus\n```\n\n#### Check whether a layer has focus\n```coffeescript\nprint layerA.focus\n```\n\n#### Overriding focus logic\nSometimes you will want to change focus in a way that doesn't make exact geometric sense. For example, when switching from a large header row to a smaller secondary row, you may prefer the first cell in the secondary row receive focus. Or, you may want to provide a way for focus to \"loop around\" at a row's end. These kinds of functions are possible through _overrides._\n\nSet a layer's overrides using the `overrides` object:\n```coffeescript\nlayerA.overrides =\n\tup: \u003clayer\u003e\n\tdown: \u003clayer\u003e\n\tleft: \u003clayer\u003e\n\tright: \u003clayer\u003e\n```\n\nIt is not necessary to set all four overrides; but only for those directions which require custom behavior. Now, shifting focus from `layerA`  will always land on the direction-specified target -- no matter what FocusEngine thinks should happen.\n\n\u003cimg src=\"https://user-images.githubusercontent.com/935/28783390-d7b1c37e-75d5-11e7-86f3-6a3712430489.png\" width=\"400\" style=\"display: block; margin: auto\" alt=\"Override illustration\" /\u003e\n\n\u003e Obviously, you can create very counterintuitive behaviors this way. Use with care! \n\nIf a layer has custom overrides, or has been initialized with FocusEngine, you may check any of its current overrides:\n```coffeescript\nprint layerA.overrides?.up\n```\n\nThe `?` permits the check to fail gracefully on layers which have no overrides.\n\n#### Enable debug mode to log focus changes\n```coffeescript\nfe.debug = true\n```\n\n### Example project\n[Download](https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/bpxl-labs/FocusEngine/tree/master/example.framer) the example to try it for yourself.\n\n### Known issues\n\nAttempting to perform a `placeFocus()` call as FocusEngine is changing its own focus will fail. (The call is discarded.) If you need to override FocusEngine's logic, use the `overrides` feature or add a slight delay to ensure the call is respected.\n\n```coffeescript\nlayerA.on \"unfocus\", -\u003e\n\tUtils.delay 0.1, -\u003e\n\t\tfe.placeFocus(layerB)\n```\n\n---\n\nWebsite: [blackpixel.com](https://blackpixel.com) \u0026nbsp;\u0026middot;\u0026nbsp;\nGitHub: [@bpxl-labs](https://github.com/bpxl-labs/) \u0026nbsp;\u0026middot;\u0026nbsp;\nTwitter: [@blackpixel](https://twitter.com/blackpixel) \u0026nbsp;\u0026middot;\u0026nbsp;\nMedium: [@bpxl-craft](https://medium.com/bpxl-craft)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgohypergiant%2FFocusEngine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgohypergiant%2FFocusEngine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgohypergiant%2FFocusEngine/lists"}