@transferwise/components 46.11.0 → 46.13.0

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.
Files changed (38) hide show
  1. package/build/i18n/zh-HK.json +2 -2
  2. package/build/index.esm.js +67 -153
  3. package/build/index.esm.js.map +1 -1
  4. package/build/index.js +67 -153
  5. package/build/index.js.map +1 -1
  6. package/build/types/drawer/Drawer.d.ts.map +1 -1
  7. package/build/types/index.d.ts +1 -0
  8. package/build/types/index.d.ts.map +1 -1
  9. package/build/types/modal/Modal.d.ts.map +1 -1
  10. package/build/types/popover/Popover.d.ts +1 -0
  11. package/build/types/popover/Popover.d.ts.map +1 -1
  12. package/build/types/typeahead/Typeahead.d.ts +95 -57
  13. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  14. package/build/types/typeahead/index.d.ts +2 -2
  15. package/build/types/typeahead/index.d.ts.map +1 -1
  16. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +23 -41
  17. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  18. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts +9 -17
  19. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts.map +1 -1
  20. package/build/types/typeahead/util/highlight.d.ts +2 -1
  21. package/build/types/typeahead/util/highlight.d.ts.map +1 -1
  22. package/package.json +5 -2
  23. package/src/drawer/Drawer.js +13 -2
  24. package/src/drawer/__snapshots__/Drawer.rtl.spec.js.snap +1 -0
  25. package/src/i18n/zh-HK.json +2 -2
  26. package/src/index.ts +1 -0
  27. package/src/modal/Modal.tsx +6 -3
  28. package/src/popover/Popover.js +7 -3
  29. package/src/popover/Popover.spec.js +16 -8
  30. package/src/popover/__snapshots__/Popover.spec.js.snap +0 -56
  31. package/src/typeahead/{Typeahead.story.js → Typeahead.story.tsx} +8 -7
  32. package/src/typeahead/{Typeahead.js → Typeahead.tsx} +110 -111
  33. package/src/typeahead/index.ts +2 -0
  34. package/src/typeahead/typeaheadInput/TypeaheadInput.spec.js +1 -0
  35. package/src/typeahead/typeaheadInput/{TypeaheadInput.js → TypeaheadInput.tsx} +35 -46
  36. package/src/typeahead/typeaheadOption/{TypeaheadOption.js → TypeaheadOption.tsx} +10 -20
  37. package/src/typeahead/util/{highlight.js → highlight.tsx} +1 -1
  38. package/src/typeahead/index.js +0 -3
@@ -4,10 +4,10 @@
4
4
  "neptune.ClearButton.ariaLabel": "清除",
5
5
  "neptune.CloseButton.ariaLabel": "關閉",
6
6
  "neptune.DateInput.day.label": "日",
7
- "neptune.DateInput.day.placeholder": "",
7
+ "neptune.DateInput.day.placeholder": "DD",
8
8
  "neptune.DateInput.month.label": "月",
9
9
  "neptune.DateInput.year.label": "年",
10
- "neptune.DateInput.year.placeholder": "",
10
+ "neptune.DateInput.year.placeholder": "YYYY",
11
11
  "neptune.DateLookup.day": "日",
12
12
  "neptune.DateLookup.goTo20YearView": "切換至20年視圖",
13
13
  "neptune.DateLookup.month": "月",
@@ -1867,6 +1867,7 @@ const Drawer = ({
1867
1867
  const {
1868
1868
  isMobile
1869
1869
  } = useLayout();
1870
+ const titleId = useId();
1870
1871
  return /*#__PURE__*/jsx(Dimmer$1, {
1871
1872
  open: open,
1872
1873
  onClose: onClose,
@@ -1874,13 +1875,16 @@ const Drawer = ({
1874
1875
  open: open,
1875
1876
  position: isMobile ? Position.BOTTOM : position,
1876
1877
  children: /*#__PURE__*/jsxs("div", {
1877
- className: classNames('np-drawer', className),
1878
1878
  role: "dialog",
1879
+ "aria-modal": true,
1880
+ "aria-labelledby": headerTitle ? titleId : undefined,
1881
+ className: classNames('np-drawer', className),
1879
1882
  children: [/*#__PURE__*/jsxs("div", {
1880
1883
  className: classNames('np-drawer-header', {
1881
1884
  'np-drawer-header--withborder': headerTitle
1882
1885
  }),
1883
1886
  children: [headerTitle && /*#__PURE__*/jsx(Title, {
1887
+ id: titleId,
1884
1888
  type: Typography.TITLE_BODY,
1885
1889
  children: headerTitle
1886
1890
  }), /*#__PURE__*/jsx(CloseButton, {
@@ -5922,6 +5926,7 @@ const Modal = ({
5922
5926
  const isCompact = checkSpecialClasses('compact');
5923
5927
  const noDivider = checkSpecialClasses('no-divider');
5924
5928
  const contentReference = useRef(null);
5929
+ const titleId = useId();
5925
5930
  return !isMedium ? /*#__PURE__*/jsx(Drawer$1, {
5926
5931
  open: open,
5927
5932
  headerTitle: title,
@@ -5949,11 +5954,12 @@ const Modal = ({
5949
5954
  className: classNames('tw-modal', 'd-flex', 'fade', 'outline-none', scroll === Scroll.CONTENT && 'tw-modal--scrollable', className),
5950
5955
  ...otherProps,
5951
5956
  children: /*#__PURE__*/jsx("div", {
5957
+ role: "dialog",
5958
+ "aria-modal": true,
5959
+ "aria-labelledby": titleId,
5952
5960
  className: classNames('tw-modal-dialog', 'd-flex', {
5953
5961
  [`tw-modal-${size}`]: size
5954
5962
  }),
5955
- "aria-modal": true,
5956
- role: "dialog",
5957
5963
  children: /*#__PURE__*/jsxs("div", {
5958
5964
  className: classNames('tw-modal-content', 'd-flex', 'flex-column', 'justify-content-between', {
5959
5965
  'tw-modal-compact': isCompact,
@@ -5964,6 +5970,7 @@ const Modal = ({
5964
5970
  'modal--withoutborder': !title || noDivider
5965
5971
  }),
5966
5972
  children: [/*#__PURE__*/jsx(Title, {
5973
+ id: titleId,
5967
5974
  type: Typography.TITLE_BODY,
5968
5975
  className: "tw-modal-title",
5969
5976
  children: title
@@ -5993,7 +6000,8 @@ const Popover$1 = ({
5993
6000
  className,
5994
6001
  content,
5995
6002
  preferredPlacement,
5996
- title
6003
+ title,
6004
+ onClose
5997
6005
  }) => {
5998
6006
  logActionRequired({
5999
6007
  preferredPlacement
@@ -6003,7 +6011,10 @@ const Popover$1 = ({
6003
6011
  const {
6004
6012
  isModern
6005
6013
  } = useTheme();
6006
- const onClose = () => setOpen(false);
6014
+ const handleOnClose = () => {
6015
+ setOpen(false);
6016
+ onClose?.();
6017
+ };
6007
6018
  return /*#__PURE__*/jsxs("span", {
6008
6019
  className: classNames('np-popover', className),
6009
6020
  children: [/*#__PURE__*/jsx("span", {
@@ -6023,7 +6034,7 @@ const Popover$1 = ({
6023
6034
  position: deprecatedPlacements[preferredPlacement] || preferredPlacement,
6024
6035
  arrow: true,
6025
6036
  className: "np-popover__container",
6026
- onClose: onClose,
6037
+ onClose: handleOnClose,
6027
6038
  children: /*#__PURE__*/jsxs("div", {
6028
6039
  className: isModern ? 'np-popover__content np-text-default-body' : 'np-popover__content',
6029
6040
  "aria-hidden": !open,
@@ -6056,6 +6067,7 @@ Popover$1.propTypes = {
6056
6067
  * `'bottom-right'` / `'bottom-left'` are deprecated use `Position.BOTTOM` / `'bottom'` instead
6057
6068
  */
6058
6069
  preferredPlacement: PropTypes.oneOf(['top', 'right', 'bottom', 'left', 'left-top', 'right-top', 'bottom-right', 'bottom-left']),
6070
+ onClose: PropTypes.func,
6059
6071
  title: PropTypes.node
6060
6072
  };
6061
6073
  const deprecatedPlacements = {
@@ -12210,8 +12222,10 @@ const TextareaWithDisplayFormat = props => /*#__PURE__*/jsx(WithDisplayFormat, {
12210
12222
  /* eslint-disable jsx-a11y/no-static-element-interactions */
12211
12223
  const DEFAULT_INPUT_MIN_WIDTH = 10;
12212
12224
  class TypeaheadInput extends Component {
12213
- constructor() {
12214
- super();
12225
+ inputRef = /*#__PURE__*/createRef();
12226
+ sizerRef = /*#__PURE__*/createRef();
12227
+ constructor(props) {
12228
+ super(props);
12215
12229
  this.state = {
12216
12230
  inputWidth: DEFAULT_INPUT_MIN_WIDTH
12217
12231
  };
@@ -12221,7 +12235,7 @@ class TypeaheadInput extends Component {
12221
12235
  autoFocus
12222
12236
  } = this.props;
12223
12237
  if (autoFocus) {
12224
- this.inputRef.focus();
12238
+ this.inputRef.current?.focus();
12225
12239
  }
12226
12240
  }
12227
12241
  componentDidUpdate(previousProps) {
@@ -12232,7 +12246,7 @@ class TypeaheadInput extends Component {
12232
12246
  recalculateWidth = () => {
12233
12247
  requestAnimationFrame(() => {
12234
12248
  this.setState({
12235
- inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.scrollWidth + 10)
12249
+ inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.current?.scrollWidth ?? 0 + 10)
12236
12250
  });
12237
12251
  });
12238
12252
  };
@@ -12257,9 +12271,7 @@ class TypeaheadInput extends Component {
12257
12271
  } = this.state;
12258
12272
  const hasPlaceholder = !multiple || selected.length === 0;
12259
12273
  return /*#__PURE__*/jsx(Input, {
12260
- ref: reference => {
12261
- this.inputRef = reference;
12262
- },
12274
+ ref: this.inputRef,
12263
12275
  className: classNames(multiple && 'typeahead__input'),
12264
12276
  name: name,
12265
12277
  id: `input-${typeaheadId}`,
@@ -12292,11 +12304,11 @@ class TypeaheadInput extends Component {
12292
12304
  } = this.props;
12293
12305
  return multiple ? /*#__PURE__*/jsxs("div", {
12294
12306
  className: "form-control typeahead__input-container",
12295
- style: maxHeight && {
12307
+ style: {
12296
12308
  maxHeight
12297
12309
  },
12298
12310
  onClick: () => {
12299
- this.inputRef.focus();
12311
+ this.inputRef.current?.focus();
12300
12312
  },
12301
12313
  children: [/*#__PURE__*/jsxs("div", {
12302
12314
  className: "typeahead__input-wrapper",
@@ -12304,39 +12316,13 @@ class TypeaheadInput extends Component {
12304
12316
  className: "typeahead__input-aligner"
12305
12317
  })]
12306
12318
  }), /*#__PURE__*/jsx("div", {
12307
- ref: reference => {
12308
- this.sizerRef = reference;
12309
- },
12319
+ ref: this.sizerRef,
12310
12320
  className: "sizer form-control typeahead__input",
12311
12321
  children: value
12312
12322
  })]
12313
12323
  }) : this.renderInput();
12314
12324
  }
12315
12325
  }
12316
- TypeaheadInput.propTypes = {
12317
- typeaheadId: PropTypes.string.isRequired,
12318
- name: PropTypes.string.isRequired,
12319
- autoFocus: PropTypes.bool,
12320
- multiple: PropTypes.bool.isRequired,
12321
- value: PropTypes.string.isRequired,
12322
- selected: PropTypes.arrayOf(PropTypes.object),
12323
- placeholder: PropTypes.string,
12324
- optionsShown: PropTypes.bool,
12325
- maxHeight: PropTypes.number,
12326
- autoComplete: PropTypes.string.isRequired,
12327
- onChange: PropTypes.func.isRequired,
12328
- renderChip: PropTypes.func.isRequired,
12329
- onKeyDown: PropTypes.func.isRequired,
12330
- onFocus: PropTypes.func.isRequired,
12331
- onPaste: PropTypes.func.isRequired
12332
- };
12333
- TypeaheadInput.defaultProps = {
12334
- autoFocus: false,
12335
- maxHeight: null,
12336
- placeholder: '',
12337
- optionsShown: false,
12338
- selected: []
12339
- };
12340
12326
 
12341
12327
  function highlight(value, query) {
12342
12328
  if (value && query) {
@@ -12357,9 +12343,9 @@ function highlight(value, query) {
12357
12343
  const Option = props => {
12358
12344
  const {
12359
12345
  option,
12360
- selected,
12361
- onClick,
12362
- query
12346
+ selected = false,
12347
+ onClick = () => {},
12348
+ query = ''
12363
12349
  } = props;
12364
12350
  const {
12365
12351
  label,
@@ -12389,46 +12375,48 @@ const Option = props => {
12389
12375
  })
12390
12376
  });
12391
12377
  };
12392
- Option.propTypes = {
12393
- option: PropTypes.shape({
12394
- label: PropTypes.string.isRequired,
12395
- note: PropTypes.string,
12396
- secondary: PropTypes.string
12397
- }).isRequired,
12398
- query: PropTypes.string,
12399
- selected: PropTypes.bool,
12400
- onClick: PropTypes.func
12401
- };
12402
- Option.defaultProps = {
12403
- selected: false,
12404
- query: '',
12405
- onClick: () => {}
12406
- };
12407
- var TypeaheadOption = Option;
12408
12378
 
12409
12379
  /* eslint-disable jsx-a11y/anchor-is-valid */
12410
12380
  /* eslint-disable jsx-a11y/click-events-have-key-events */
12411
12381
  /* eslint-disable jsx-a11y/no-static-element-interactions */
12412
-
12413
12382
  const DEFAULT_MIN_QUERY_LENGTH = 3;
12414
12383
  const SEARCH_DELAY = 200;
12415
12384
  class Typeahead extends Component {
12385
+ static defaultProps = {
12386
+ allowNew: false,
12387
+ autoFillOnBlur: true,
12388
+ autoFocus: false,
12389
+ chipSeparators: [],
12390
+ clearable: true,
12391
+ initialValue: [],
12392
+ inputAutoComplete: 'new-password',
12393
+ minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
12394
+ multiple: false,
12395
+ searchDelay: SEARCH_DELAY,
12396
+ showSuggestions: true,
12397
+ showNewEntry: true,
12398
+ size: Size.MEDIUM,
12399
+ validateChip: () => true
12400
+ };
12416
12401
  constructor(props) {
12417
12402
  super(props);
12418
12403
  const {
12419
12404
  searchDelay,
12420
12405
  initialValue,
12421
12406
  multiple
12422
- } = props;
12407
+ } = this.props;
12423
12408
  this.handleSearchDebounced = debounce(this.handleSearch, searchDelay);
12424
12409
  const initialQuery = !multiple && initialValue.length > 0 ? initialValue[0].label : '';
12425
12410
  this.state = {
12426
12411
  selected: initialValue,
12427
12412
  errorState: false,
12428
12413
  query: initialQuery,
12429
- keyboardFocusedOptionIndex: null
12414
+ keyboardFocusedOptionIndex: null,
12415
+ optionsShown: false,
12416
+ isFocused: false
12430
12417
  };
12431
12418
  }
12419
+ handleSearchDebounced;
12432
12420
  UNSAFE_componentWillReceiveProps(nextProps) {
12433
12421
  if (nextProps.multiple !== this.props.multiple) {
12434
12422
  this.setState(previousState => {
@@ -12442,6 +12430,7 @@ class Typeahead extends Component {
12442
12430
  };
12443
12431
  }
12444
12432
  return {
12433
+ selected: previousState.selected,
12445
12434
  query: ''
12446
12435
  };
12447
12436
  });
@@ -12451,13 +12440,8 @@ class Typeahead extends Component {
12451
12440
  this.handleSearchDebounced.cancel();
12452
12441
  }
12453
12442
  handleOnFocus = () => {
12454
- const {
12455
- onFocus
12456
- } = this.props;
12457
12443
  this.showMenu();
12458
- if (onFocus) {
12459
- this.props.onFocus();
12460
- }
12444
+ this.props.onFocus?.();
12461
12445
  };
12462
12446
  onOptionSelected = (event, item) => {
12463
12447
  event.preventDefault();
@@ -12540,7 +12524,7 @@ class Typeahead extends Component {
12540
12524
  break;
12541
12525
  case 'Enter':
12542
12526
  event.preventDefault();
12543
- if (options[keyboardFocusedOptionIndex]) {
12527
+ if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
12544
12528
  this.selectItem(options[keyboardFocusedOptionIndex]);
12545
12529
  } else if (allowNew && query.trim()) {
12546
12530
  this.selectItem({
@@ -12592,13 +12576,6 @@ class Typeahead extends Component {
12592
12576
  query
12593
12577
  });
12594
12578
  };
12595
- stopPropagation = event => {
12596
- event.stopPropagation();
12597
- event.preventDefault();
12598
- if (event.nativeEvent && event.nativeEvent.stopImmediatePropagation) {
12599
- event.nativeEvent.stopImmediatePropagation();
12600
- }
12601
- };
12602
12579
  handleSearch = query => {
12603
12580
  const {
12604
12581
  onSearch
@@ -12683,7 +12660,7 @@ class Typeahead extends Component {
12683
12660
  }
12684
12661
  };
12685
12662
  renderChip = (option, idx) => {
12686
- const valid = this.props.validateChip(option);
12663
+ const valid = this.props.validateChip?.(option);
12687
12664
  return /*#__PURE__*/jsx(Chip, {
12688
12665
  label: option.label,
12689
12666
  className: classNames('m-t-1', {
@@ -12718,7 +12695,7 @@ class Typeahead extends Component {
12718
12695
  className: "dropdown-menu",
12719
12696
  role: "menu",
12720
12697
  "aria-labelledby": "dropdownMenu1",
12721
- children: [optionsToRender.map((option, idx) => /*#__PURE__*/jsx(TypeaheadOption, {
12698
+ children: [optionsToRender.map((option, idx) => /*#__PURE__*/jsx(Option, {
12722
12699
  query: query,
12723
12700
  option: option,
12724
12701
  selected: keyboardFocusedOptionIndex === idx,
@@ -12761,16 +12738,18 @@ class Typeahead extends Component {
12761
12738
  const menu = this.renderMenu({
12762
12739
  footer,
12763
12740
  options,
12741
+ id,
12764
12742
  keyboardFocusedOptionIndex,
12765
12743
  query,
12766
12744
  allowNew,
12767
12745
  showNewEntry,
12768
12746
  dropdownOpen
12769
12747
  });
12770
- const hasError = errorState || alert && alert.type === Sentiment.ERROR;
12771
- const displayAlert = !errorState && alert || alert && alert.type === Sentiment.ERROR;
12772
- const hasWarning = displayAlert && alert.type === Sentiment.WARNING;
12773
- const hasInfo = displayAlert && alert.type === Sentiment.NEUTRAL;
12748
+ const alertType = alert?.type ?? Sentiment.NEUTRAL;
12749
+ const hasError = errorState || alert && alertType === Sentiment.ERROR;
12750
+ const displayAlert = !errorState && alert || alert && alertType === Sentiment.ERROR;
12751
+ const hasWarning = displayAlert && alertType === Sentiment.WARNING;
12752
+ const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
12774
12753
  return /*#__PURE__*/jsx("div", {
12775
12754
  id: id,
12776
12755
  className: classNames('typeahead', `typeahead-${size}`, {
@@ -12779,7 +12758,7 @@ class Typeahead extends Component {
12779
12758
  'typeahead--multiple': multiple,
12780
12759
  open: dropdownOpen
12781
12760
  }),
12782
- onClick: this.stopPropagation,
12761
+ onClick: stopPropagation$1,
12783
12762
  children: /*#__PURE__*/jsxs("div", {
12784
12763
  className: classNames('form-group', {
12785
12764
  'has-error': hasError,
@@ -12800,6 +12779,7 @@ class Typeahead extends Component {
12800
12779
  placeholder,
12801
12780
  selected,
12802
12781
  maxHeight,
12782
+ id: id,
12803
12783
  name: name,
12804
12784
  value: query,
12805
12785
  typeaheadId: id,
@@ -12826,72 +12806,6 @@ class Typeahead extends Component {
12826
12806
  });
12827
12807
  }
12828
12808
  }
12829
- Typeahead.propTypes = {
12830
- id: PropTypes.string.isRequired,
12831
- name: PropTypes.string.isRequired,
12832
- options: PropTypes.arrayOf(PropTypes.shape({
12833
- label: PropTypes.string.isRequired,
12834
- note: PropTypes.string,
12835
- secondary: PropTypes.string,
12836
- value: PropTypes.object
12837
- })).isRequired,
12838
- initialValue: PropTypes.arrayOf(PropTypes.shape({
12839
- label: PropTypes.string.isRequired,
12840
- note: PropTypes.string,
12841
- secondary: PropTypes.string
12842
- })),
12843
- onChange: PropTypes.func.isRequired,
12844
- allowNew: PropTypes.bool,
12845
- autoFocus: PropTypes.bool,
12846
- clearable: PropTypes.bool,
12847
- multiple: PropTypes.bool,
12848
- showSuggestions: PropTypes.bool,
12849
- showNewEntry: PropTypes.bool,
12850
- searchDelay: PropTypes.number,
12851
- maxHeight: PropTypes.number,
12852
- minQueryLength: PropTypes.number,
12853
- addon: PropTypes.node,
12854
- placeholder: PropTypes.string,
12855
- alert: PropTypes.shape({
12856
- message: PropTypes.string.isRequired,
12857
- type: PropTypes.oneOf(['error', 'warning', 'neutral']).isRequired
12858
- }),
12859
- footer: PropTypes.node,
12860
- validateChip: PropTypes.func,
12861
- onSearch: PropTypes.func,
12862
- onBlur: PropTypes.func,
12863
- onInputChange: PropTypes.func,
12864
- onFocus: PropTypes.func,
12865
- chipSeparators: PropTypes.arrayOf(PropTypes.string),
12866
- size: PropTypes.oneOf(['md', 'lg']),
12867
- inputAutoComplete: PropTypes.string,
12868
- autoFillOnBlur: PropTypes.bool
12869
- };
12870
- Typeahead.defaultProps = {
12871
- allowNew: false,
12872
- autoFocus: false,
12873
- clearable: true,
12874
- multiple: false,
12875
- maxHeight: null,
12876
- showSuggestions: true,
12877
- showNewEntry: true,
12878
- searchDelay: SEARCH_DELAY,
12879
- minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
12880
- addon: null,
12881
- placeholder: null,
12882
- alert: null,
12883
- footer: null,
12884
- size: Size.MEDIUM,
12885
- chipSeparators: [],
12886
- initialValue: [],
12887
- onSearch: null,
12888
- onBlur: null,
12889
- onInputChange: null,
12890
- onFocus: null,
12891
- validateChip: () => true,
12892
- inputAutoComplete: 'new-password',
12893
- autoFillOnBlur: true
12894
- };
12895
12809
 
12896
12810
  // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
12897
12811
  var UploadStep;
@@ -15440,10 +15354,10 @@ var zhHK = {
15440
15354
  "neptune.ClearButton.ariaLabel": "清除",
15441
15355
  "neptune.CloseButton.ariaLabel": "關閉",
15442
15356
  "neptune.DateInput.day.label": "日",
15443
- "neptune.DateInput.day.placeholder": "",
15357
+ "neptune.DateInput.day.placeholder": "DD",
15444
15358
  "neptune.DateInput.month.label": "月",
15445
15359
  "neptune.DateInput.year.label": "年",
15446
- "neptune.DateInput.year.placeholder": "",
15360
+ "neptune.DateInput.year.placeholder": "YYYY",
15447
15361
  "neptune.DateLookup.day": "日",
15448
15362
  "neptune.DateLookup.goTo20YearView": "切換至20年視圖",
15449
15363
  "neptune.DateLookup.month": "月",