├── .DS_Store ├── .github ├── ISSUE_TEMPLATE └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── blink_impl ├── heuristic_default_move.html ├── heuristic_scrollable.html ├── list.md ├── style.css └── testcase_style.css ├── demo ├── .DS_Store ├── blog │ ├── blog-style.css │ ├── blog-utils.js │ ├── images │ │ ├── feed │ │ │ ├── 1.jpg │ │ │ ├── 10.jpg │ │ │ ├── 11.jpg │ │ │ ├── 12.jpg │ │ │ ├── 13.jpg │ │ │ ├── 14.jpg │ │ │ ├── 2.jpg │ │ │ ├── 3.jpg │ │ │ ├── 4.jpg │ │ │ ├── 5.jpg │ │ │ ├── 6.jpg │ │ │ ├── 7.jpg │ │ │ ├── 8.jpg │ │ │ └── 9.jpg │ │ └── profile.png │ ├── index.html │ └── thumbnail.png ├── calendar │ ├── images │ │ └── pattern.png │ ├── index.html │ ├── style.css │ ├── thumbnail.png │ └── thumbnail_.png ├── favicon.png ├── index.html ├── infinite-scroller │ ├── index.html │ ├── scroller-style.css │ └── scroller-utils.js ├── link.png ├── mark.png ├── sample │ ├── api_focusableAreas.html │ ├── api_getSpatialNavigationContainer.html │ ├── api_navigate.html │ ├── api_navigate_event.html │ ├── api_prevent_navbeforefocus.html │ ├── api_prevent_navnotarget.html │ ├── api_spatialNavigationSearch.html │ ├── api_spatial_navigation_action.html │ ├── api_spatial_navigation_contain.html │ ├── api_spatial_navigation_empty_nested_container.html │ ├── api_spatial_navigation_function.html │ ├── api_spatial_navigation_nested_container.html │ ├── heurisitic_candidates_same_distance.html │ ├── heuristic_default_move.html │ ├── heuristic_distance_function_grid_001.html │ ├── heuristic_distance_function_grid_002.html │ ├── heuristic_distance_function_grid_003.html │ ├── heuristic_drawing_board.html │ ├── heuristic_grid_layout.html │ ├── heuristic_iframe.html │ ├── heuristic_iframe_sub.html │ ├── heuristic_inert_attribute.html │ ├── heuristic_input_elements.html │ ├── heuristic_non_scrollable_iframe.html │ ├── heuristic_non_scrollable_iframe_element.html │ ├── heuristic_overflow_regions.html │ ├── heuristic_overlapped_elements.html │ ├── heuristic_role_attribute.html │ ├── heuristic_scrollable.html │ ├── heuristic_scrollable_test.html │ ├── heuristic_starting_point.html │ ├── heuristic_text_type_elements.html │ ├── heuristic_textarea.html │ ├── images │ │ └── login_img.png │ ├── sample_focusout_event_order.html │ ├── sample_shadowDOM_SpatNav.html │ ├── sample_shadowDOM_SpatNav_iframe.html │ ├── spatnav-style.css │ └── spatnav-utils.js └── style.css ├── docs ├── Spatial Navigation in the Web (LG Electronics).pdf ├── images_of_spec.pptx ├── spatialNavigationSearch_explainer.md └── spatnav_processing_model_diagram.xml ├── images ├── calender1.png ├── calender2.png ├── exit-entry-point.JPG ├── nav-rule-example.png ├── patterns-navigational-transitions-parent-to-child-calendar-tablet-xhdpi-004.webm ├── pinterest.png └── scroll related terms.png ├── implStatus.md ├── package-lock.json ├── package.json ├── polyfill ├── LICENSE ├── README.md ├── changelog.md └── spatial-navigation-polyfill.js ├── resources ├── testharness.css ├── testharness.js └── testharnessreport.js ├── tests ├── README.md ├── internal │ ├── api-test.html │ ├── calendar.css │ ├── event-test.html │ ├── focusable-test.html │ ├── hostile_iframe1.html │ ├── hostile_iframe2.html │ ├── hostile_iframe3.html │ ├── hostile_iframe_test.html │ ├── iframe-offscreen-test.html │ ├── iframe-test.html │ ├── iframe_sub.html │ ├── iframe_sub2.html │ ├── isVisibleInScroller-test.html │ ├── out-of-viewport.html │ ├── overlapped-focusable-test.html │ ├── scrollable-container-test.html │ ├── search-origin-offscreen-test.html │ ├── search-origin-vanish-test.html │ ├── spatial-navigation-action-test.html │ ├── spatial-navigation-function-test.html │ ├── spatialNavSearch-test.html │ ├── test.css │ └── test.js ├── spatnav-focus-001.html ├── spatnav-scroll-001.html └── ux │ ├── README.md │ ├── list.md │ ├── result.md │ ├── spatnav-algorithm-test.js │ ├── spatnav-distance-function-fragments-001.html │ ├── spatnav-distance-function-grid-001.html │ ├── spatnav-distance-function-grid-002.html │ ├── spatnav-distance-function-grid-003.html │ ├── spatnav-distance-function-grid-004.html │ ├── spatnav-distance-function-grid-align-001.html │ ├── spatnav-distance-function-grid-align-002.html │ ├── spatnav-distance-function-grid-align-003.html │ ├── spatnav-distance-function-grid-align-004.html │ ├── spatnav-distance-function-intersected-001.html │ ├── spatnav-distance-function-intersected-002.html │ ├── spatnav-distance-function-transformed-001.html │ ├── spatnav-drowing-board.html │ └── spatnav-simple-test.html ├── tools └── chrome-extension │ ├── LICENSE │ ├── README.md │ ├── icon.png │ ├── img1.png │ ├── img2.gif │ ├── img3.png │ ├── manifest.json │ ├── package-lock.json │ ├── package.json │ ├── polyfill │ └── spatial-navigation-polyfill.js │ └── src │ ├── .eslintrc.js │ ├── Panel.html │ ├── background.js │ ├── content_script.css │ ├── content_script.js │ ├── detector │ ├── detector.js │ ├── graph.js │ ├── isolation_elements_detector.js │ ├── loop_elements_detector.js │ ├── trap_elements_detector.js │ └── unreachable_elements_detector.js │ ├── devtool.html │ ├── devtool.js │ ├── panel.css │ ├── panel.js │ ├── popup.css │ ├── popup.html │ └── popup.js └── w3c.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/spatial-navigation/11ffdb06637c0f86cb8eb5f430bad0a26b547184/.DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | The specification has been moved to https://drafts.csswg.org/css-nav-1/ 2 | If you want to report issues about the specification, please report them at: 3 | https://github.com/w3c/csswg-drafts/labels/css-nav-1 4 | 5 | Issues about the polyfill, or tests, or demos can be posted here. 6 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '35 6 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | blink_impl 2 | docs 3 | dist 4 | node_modules 5 | .gitignore 6 | .eslintrc.js 7 | .eslintrc.yml 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .npmignore 3 | .gitignore 4 | .eslintrc.js 5 | .eslintrc.yml 6 | implStatus.md 7 | CODE_OF_CONDUCT.md 8 | CONTRIBUTING.md 9 | LICENSE.md 10 | package-lock.json 11 | blink_impl/ 12 | docs/ 13 | images/ 14 | resources/ 15 | tests/ 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | As a W3C Community Group, the WICG operates under the W3C's [Code of Ethics and Professional Conduct](http://www.w3.org/Consortium/cepc/). 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Platform Incubator 4 | Community Group, governed by the [W3C Community License Agreement 5 | (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive 6 | contributions, you must join the CG. 7 | 8 | If you are not the sole contributor to a contribution (pull request), please 9 | identify all contributors in the pull request comment. 10 | 11 | To add a contributor (other than yourself, that's automatic), mark them one per 12 | line as follows: 13 | 14 | ``` 15 | +@github_username 16 | ``` 17 | 18 | If you added a contributor by mistake, you can remove them in a comment with: 19 | 20 | ``` 21 | -@github_username 22 | ``` 23 | 24 | If you are making a pull request on behalf of someone else but you had no part 25 | in designing the feature, you can remove yourself with the above syntax. 26 | 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors under the 2 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 3 | 4 | Contributions to Specifications are made under the 5 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 6 | 7 | -------------------------------------------------------------------------------- /blink_impl/heuristic_default_move.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 |This demo shows the infinite scroll in Spatial Navigation.
21 |42 | Related Github Issue 43 |
44 | 45 | 82 | 83 | -------------------------------------------------------------------------------- /demo/infinite-scroller/scroller-style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(https://themes.googleusercontent.com/static/fonts/roboto/v11/2UX7WLTfW3W8TclTUvlFyQ.woff) 6 | } 7 | 8 | body { 9 | margin: 20px; 10 | font-family: 'Roboto'; 11 | text-align: center; 12 | } 13 | 14 | h1 { 15 | text-align: center; 16 | } 17 | 18 | .description, .option { 19 | margin: 20px auto; 20 | text-align: justify; 21 | } 22 | .description { 23 | width: 80%; 24 | } 25 | .option { 26 | width: 750px; 27 | } 28 | 29 | .cssSyntax { 30 | background-color: lightsteelblue; 31 | border: 2px solid steelblue; 32 | padding: 20px; 33 | width: 500px; 34 | } 35 | 36 | #feed { 37 | display: grid; 38 | overflow-y: scroll; 39 | grid-template-columns: repeat(2, 1fr); 40 | background-color: #fff; 41 | color: #444; 42 | height: 500px; 43 | width: 500px; 44 | margin: auto auto; 45 | border: 3px solid lightgrey; 46 | } 47 | 48 | .item { 49 | height: 150px; 50 | width: 150px; 51 | margin: 60px 20px; 52 | justify-self: center; 53 | background-color: mediumseagreen; 54 | } 55 | 56 | .item p { 57 | vertical-align: middle; 58 | font-size: 36px; 59 | } 60 | 61 | :focus { 62 | outline: 5px solid #428bca; 63 | } 64 | -------------------------------------------------------------------------------- /demo/infinite-scroller/scroller-utils.js: -------------------------------------------------------------------------------- 1 | function addBoxes(root, amount, dir) { 2 | const cnt = root.childElementCount; 3 | let initCnt = 0; 4 | let endCnt = 0; 5 | 6 | if (cnt) { 7 | initCnt = parseInt(root.firstChild.innerText); 8 | endCnt = parseInt(root.lastChild.innerText); 9 | } 10 | 11 | for (let i = 1; i <= amount; i++) { 12 | let temp = document.createElement('div'); 13 | temp.setAttribute('class', 'item'); 14 | temp.setAttribute('tabindex', 0); 15 | temp.style.setProperty('--spatial-navigation-action', 'focus'); 16 | let para = document.createElement('p'); 17 | temp.appendChild(para); 18 | 19 | if (dir === 'up') { 20 | para.appendChild(document.createTextNode(`${initCnt - i}`)); 21 | root.prepend(temp); 22 | } else { 23 | para.appendChild(document.createTextNode(`${endCnt + i}`)); 24 | root.append(temp); 25 | } 26 | } 27 | 28 | return new Promise(function (resolve) { 29 | resolve(cnt + amount); 30 | }); 31 | } 32 | 33 | function updateBoxes(root, amount) { 34 | const childNum = root.childElementCount; 35 | 36 | if (childNum > amount) { 37 | for (let i = 0; i < childNum - amount; i++) { 38 | root.removeChild(root.lastChild); 39 | } 40 | } 41 | else { 42 | addBoxes(root, amount - childNum); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /demo/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/spatial-navigation/11ffdb06637c0f86cb8eb5f430bad0a26b547184/demo/link.png -------------------------------------------------------------------------------- /demo/mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/spatial-navigation/11ffdb06637c0f86cb8eb5f430bad0a26b547184/demo/mark.png -------------------------------------------------------------------------------- /demo/sample/api_focusableAreas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 56 | 57 | 58 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/demo/sample/api_getSpatialNavigationContainer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
43 |
44 |
45 |
65 |
66 | focusable.addEventListener('focus', function(e) {
67 | let spatnavContainer = e.target.getSpatialNavigationContainer();
68 | spatnavContainer.style.background = '#F0808099';
69 | spatnavContainer.style.outline = '5px red solid';
70 | console.log(spatnavContainer);
71 | });
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/demo/sample/api_navigate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
32 |
33 |
34 |
56 |
57 | const redContainer = document.getElementsByClassName("container c1")[0];
58 | redContainer.addEventListener('keydown', function(e) {
59 | const dir = WASD_KEY_CODE[e.keyCode];
60 | if(window.navigate && dir) {
61 | window.navigate(dir);
62 | e.preventDefault();
63 | }
64 | }, true);
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/demo/sample/api_navigate_event.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut 35 | labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris 36 | nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit 37 | esse cillum dolore eu fugiat nulla pariatur.
38 |