@daoyi/nmtl-ui 2.0.1-beta.107 → 2.0.1-beta.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { a as _slicedToArray } from './_rollupPluginBabelHelpers-a52356f6.js';
2
- import React__default, { useState, useEffect } from 'react';
2
+ import React__default, { useState, useEffect, useRef } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import { c as classNames } from './index-b76a85f4.js';
5
5
  import { M as Modal } from './Modal-777fe87d.js';
@@ -65,23 +65,57 @@ const KeywordList = _ref => {
65
65
  onMoreClick,
66
66
  onKeywordClick
67
67
  } = _ref;
68
- return Array.isArray(keywords) && /*#__PURE__*/React__default.createElement("div", {
69
- className: classNames('keywordList', className)
70
- }, (limit ? keywords.slice(0, limit) : keywords).map((keyword, i) => /*#__PURE__*/React__default.createElement(Typography, {
68
+ /* WCAG 2.4.3 roving tabindex(實測回饋 R32):關鍵字 chip 原本各自 tabIndex=0,
69
+ 一張卡片 N 個關鍵字=N 個 Tab 站點,鍵盤使用者要逐一跳過才能離開卡片。
70
+ 改為整組僅一個 Tab 站點(role=group),組內以方向鍵/Home/End 瀏覽,
71
+ Tab 直接離開整組。瀏覽模式(NVDA 上下鍵)不受影響。 */
72
+ const [current, setCurrent] = useState(0);
73
+ const itemRefs = useRef([]);
74
+ if (!Array.isArray(keywords)) return null;
75
+ const items = limit ? keywords.slice(0, limit) : keywords;
76
+ const hasMore = !!(limit && keywords.length > limit);
77
+ const count = items.length + (hasMore ? 1 : 0);
78
+ // 關鍵字資料變動使 current 超界時退回第一個
79
+ const activeIndex = current < count ? current : 0;
80
+ const moveTo = (event, next) => {
81
+ event.preventDefault();
82
+ const target = (next + count) % count;
83
+ setCurrent(target);
84
+ const el = itemRefs.current[target];
85
+ if (el) el.focus();
86
+ };
87
+ const handleRovingKeyDown = (event, index) => {
88
+ if (event.key === 'ArrowRight' || event.key === 'ArrowDown') moveTo(event, index + 1);else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') moveTo(event, index - 1);else if (event.key === 'Home') moveTo(event, 0);else if (event.key === 'End') moveTo(event, count - 1);
89
+ };
90
+ return /*#__PURE__*/React__default.createElement("div", {
91
+ className: classNames('keywordList', className),
92
+ role: "group",
93
+ "aria-label": getIntl().formatMessage({
94
+ id: 'aria.keyword.groupLabel'
95
+ }, {
96
+ count: keywords.length
97
+ })
98
+ }, items.map((keyword, i) => /*#__PURE__*/React__default.createElement(Typography, {
71
99
  color: "textSecondary",
72
100
  className: "keywordList__item",
73
101
  key: i.toString(),
74
102
  role: "button",
75
- tabIndex: 0,
103
+ tabIndex: activeIndex === i ? 0 : -1,
104
+ ref: el => {
105
+ itemRefs.current[i] = el;
106
+ },
76
107
  "aria-label": `${keyword}${getIntl().formatMessage({
77
108
  id: 'aria.keyword.searchHint'
78
109
  })}`,
79
110
  onClick: () => onKeywordClick(keyword),
111
+ onFocus: () => setCurrent(i),
80
112
  onKeyDown: event => {
81
113
  if (event.key === 'Enter' || event.key === ' ') {
82
114
  event.preventDefault();
83
115
  onKeywordClick(keyword);
116
+ return;
84
117
  }
118
+ handleRovingKeyDown(event, i);
85
119
  }
86
120
  }, /*#__PURE__*/React__default.createElement("div", {
87
121
  style: {
@@ -89,23 +123,29 @@ const KeywordList = _ref => {
89
123
  backgroundColor
90
124
  },
91
125
  className: "keywordList__item__hover"
92
- }, keyword))), limit && keywords.length > limit && /*#__PURE__*/React__default.createElement("div", {
126
+ }, keyword))), hasMore && /*#__PURE__*/React__default.createElement("div", {
93
127
  className: classNames('keywordList__item', 'keywordList__icon'),
94
128
  style: {
95
129
  backgroundColor
96
130
  },
97
131
  role: "button",
98
- tabIndex: 0,
132
+ tabIndex: activeIndex === items.length ? 0 : -1,
133
+ ref: el => {
134
+ itemRefs.current[items.length] = el;
135
+ },
99
136
  "aria-haspopup": "dialog",
100
137
  "aria-label": getIntl().formatMessage({
101
138
  id: 'aria.keyword.more'
102
139
  }),
103
140
  onClick: onMoreClick,
141
+ onFocus: () => setCurrent(items.length),
104
142
  onKeyDown: event => {
105
143
  if (event.key === 'Enter' || event.key === ' ') {
106
144
  event.preventDefault();
107
145
  onMoreClick();
146
+ return;
108
147
  }
148
+ handleRovingKeyDown(event, items.length);
109
149
  }
110
150
  }, /*#__PURE__*/React__default.createElement("div", {
111
151
  className: "keywordList__item__hover",
@@ -89,7 +89,8 @@ var zhtw = {
89
89
  "aria.pagination.page": "第 {page} 頁",
90
90
  "aria.pagination.pageCurrent": "第 {page} 頁,目前頁",
91
91
  "aria.pagination.select": "選擇頁碼,目前在第 {page} 頁,共 {totalPage} 頁(選擇後結果將更新)",
92
- "aria.pagination.changed": "已切換至第 {page} 頁,結果已更新"
92
+ "aria.pagination.changed": "已切換至第 {page} 頁,結果已更新",
93
+ "aria.keyword.groupLabel": "關鍵字,共 {count} 個,可用方向鍵瀏覽"
93
94
  };
94
95
 
95
96
  var zhcn = {
@@ -167,7 +168,8 @@ var zhcn = {
167
168
  "aria.pagination.page": "第 {page} 页",
168
169
  "aria.pagination.pageCurrent": "第 {page} 页,目前页",
169
170
  "aria.pagination.select": "选择页码,目前在第 {page} 页,共 {totalPage} 页(选择后结果将更新)",
170
- "aria.pagination.changed": "已切换至第 {page} 页,结果已更新"
171
+ "aria.pagination.changed": "已切换至第 {page} 页,结果已更新",
172
+ "aria.keyword.groupLabel": "关键字,共 {count} 个,可用方向键浏览"
171
173
  };
172
174
 
173
175
  var enus = {
@@ -245,7 +247,8 @@ var enus = {
245
247
  "aria.pagination.page": "Page {page}",
246
248
  "aria.pagination.pageCurrent": "Page {page}, current page",
247
249
  "aria.pagination.select": "Select page, currently on page {page} of {totalPage} (results update after selection)",
248
- "aria.pagination.changed": "Moved to page {page}. Results updated"
250
+ "aria.pagination.changed": "Moved to page {page}. Results updated",
251
+ "aria.keyword.groupLabel": "Keywords, {count} items, use arrow keys to browse"
249
252
  };
250
253
 
251
254
  var _languages;
@@ -267,9 +270,15 @@ var getIntl = function getIntl() {
267
270
  getLocale: function getLocale() {
268
271
  return lang;
269
272
  },
270
- formatMessage: function formatMessage(_ref) {
273
+ /* 支援 {var} 佔位符代入(R30):原本只查表不代入,
274
+ 「第 {page} 頁」會被報讀軟體直接唸成「第page頁」 */
275
+ formatMessage: function formatMessage(_ref, values) {
271
276
  var id = _ref.id;
272
- return (languages[lang] || zhtw)[id] || id;
277
+ var message = (languages[lang] || zhtw)[id] || id;
278
+ if (!values) return message;
279
+ return message.replace(/\{(\w+)\}/g, function (match, key) {
280
+ return values[key] === undefined ? match : String(values[key]);
281
+ });
273
282
  }
274
283
  };
275
284
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daoyi/nmtl-ui",
3
- "version": "2.0.1-beta.107",
3
+ "version": "2.0.1-beta.109",
4
4
  "description": "This project is to be used as a starter kit for building React component using Storybook and Rollup",
5
5
  "author": "daoyi",
6
6
  "main": "lib/index.js",