@oslokommune/punkt-react 9.4.0 → 9.5.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.
@@ -0,0 +1,150 @@
1
+ import React, { forwardRef, HTMLProps } from 'react'
2
+
3
+ import { PktButton } from '../button/Button'
4
+
5
+ interface SearchSuggestion {
6
+ title?: string
7
+ text?: string
8
+ href?: string
9
+ onClick?: () => void
10
+ }
11
+
12
+ interface ISearchInput extends HTMLProps<HTMLFormElement & HTMLDivElement> {
13
+ action?: string
14
+ appearance?: 'local' | 'local-with-button' | 'global'
15
+ disabled?: boolean
16
+ fullwidth?: boolean
17
+ id: string
18
+ label?: string
19
+ method?: 'get' | 'post' | 'dialog'
20
+ name?: string
21
+ placeholder?: string
22
+ suggestions?: SearchSuggestion[]
23
+ value?: string | undefined
24
+ onSearch?: (value: string) => void
25
+ onSuggestionClick?: (index: number) => void
26
+ }
27
+
28
+ export const PktSearchInput = forwardRef<HTMLFormElement & HTMLDivElement, ISearchInput>(
29
+ (
30
+ {
31
+ action,
32
+ appearance = 'local',
33
+ disabled = false,
34
+ fullwidth = false,
35
+ id,
36
+ label,
37
+ method = 'get',
38
+ name,
39
+ placeholder = 'Søk…',
40
+ suggestions,
41
+ value = '',
42
+ onSearch,
43
+ onSuggestionClick,
44
+ ...props
45
+ },
46
+ ref,
47
+ ) => {
48
+ const handleSuggestionClick = (cb: (() => void) | undefined, index: number) => {
49
+ if (cb) {
50
+ cb()
51
+ } else if (onSuggestionClick) {
52
+ onSuggestionClick(index)
53
+ }
54
+ }
55
+
56
+ const wrapperClass = `pkt-searchinput pkt-searchinput--${appearance} ${
57
+ fullwidth ? 'pkt-searchinput--fullwidth' : ''
58
+ }`
59
+
60
+ const WrapperElement = action ? 'form' : 'div'
61
+ const LabelElement = label ? 'label' : 'div'
62
+
63
+ return (
64
+ <WrapperElement
65
+ className={wrapperClass}
66
+ onSubmit={onSearch && (() => onSearch(value))}
67
+ action={action ? action : undefined}
68
+ method={action ? method : undefined}
69
+ role="search"
70
+ ref={ref}
71
+ {...props}
72
+ >
73
+ <LabelElement
74
+ htmlFor={label ? id : undefined}
75
+ className={label ? 'pkt-inputwrapper__label' : ''}
76
+ >
77
+ {label && <>{label}</>}
78
+ <div
79
+ className={appearance === 'local' ? 'pkt-input__container' : 'pkt-searchinput__field'}
80
+ >
81
+ <input
82
+ className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
83
+ type="search"
84
+ name={name || id}
85
+ id={id}
86
+ placeholder={placeholder}
87
+ value={value}
88
+ disabled={disabled}
89
+ onInput={onSearch && ((event) => onSearch(event.currentTarget.value))}
90
+ autoComplete="off"
91
+ aria-autocomplete="list"
92
+ aria-controls={`${id}-suggestions`}
93
+ />
94
+ <PktButton
95
+ className={`pkt-searchinput__button ${
96
+ appearance === 'local' ? 'pkt-input-icon' : ''
97
+ }`}
98
+ variant="icon-only"
99
+ iconName="magnifying-glass-big"
100
+ skin={appearance === 'local' ? 'tertiary' : 'primary'}
101
+ color={appearance === 'global' ? 'yellow' : undefined}
102
+ type="submit"
103
+ disabled={disabled}
104
+ onClick={onSearch && (() => onSearch(value))}
105
+ onKeyUp={onSearch && ((event) => event.key === 'Enter' && onSearch(value))}
106
+ >
107
+ {label || placeholder}
108
+ </PktButton>
109
+ </div>
110
+ </LabelElement>
111
+ {suggestions && (
112
+ <ul
113
+ id={`${id}-suggestions`}
114
+ className="pkt-searchinput__suggestions"
115
+ aria-live="assertive"
116
+ >
117
+ {suggestions.map((suggestion, index) => (
118
+ <li key={`search-suggestion-${index}`}>
119
+ {React.createElement(
120
+ suggestion.href ? 'a' : suggestion.onClick ? 'button' : 'div',
121
+ {
122
+ href: suggestion.href,
123
+ className: `pkt-searchinput__suggestion ${
124
+ suggestion.onClick ? 'pkt-link-button' : ''
125
+ } ${
126
+ suggestion.href || suggestion.onClick
127
+ ? 'pkt-searchinput__suggestion--has-hover'
128
+ : ''
129
+ }`,
130
+ type: suggestion.onClick ? 'button' : undefined,
131
+ onClick: () => handleSuggestionClick(suggestion.onClick, index),
132
+ onKeyUp: () => handleSuggestionClick(suggestion.onClick, index),
133
+ },
134
+ <>
135
+ {suggestion.title && (
136
+ <h3 className="pkt-searchinput__suggestion-title">{suggestion.title}</h3>
137
+ )}
138
+ {suggestion.text && (
139
+ <p className="pkt-searchinput__suggestion-text">{suggestion.text}</p>
140
+ )}
141
+ </>,
142
+ )}
143
+ </li>
144
+ ))}
145
+ </ul>
146
+ )}
147
+ </WrapperElement>
148
+ )
149
+ },
150
+ )
@@ -29,6 +29,7 @@ export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
29
29
  type?: string
30
30
  useWrapper?: boolean
31
31
  value?: string
32
+ omitSearchIcon?: boolean
32
33
  }
33
34
 
34
35
  export const PktTextinput = forwardRef(
@@ -59,6 +60,7 @@ export const PktTextinput = forwardRef(
59
60
  suffix,
60
61
  type = 'text',
61
62
  useWrapper = true,
63
+ omitSearchIcon = false,
62
64
  value,
63
65
  ...props
64
66
  }: IPktTextinput,
@@ -66,6 +68,7 @@ export const PktTextinput = forwardRef(
66
68
  ) => {
67
69
  const classNames = [className, 'pkt-textinput'].join(' ')
68
70
  const labelledBy = ariaLabelledby || `${id}-label`
71
+ const shouldShowSearchIcon = type === 'search' && !iconNameRight && !omitSearchIcon
69
72
  return (
70
73
  <PktInputWrapper
71
74
  ariaDescribedby={ariaDescribedby}
@@ -106,10 +109,16 @@ export const PktTextinput = forwardRef(
106
109
  <p className="pkt-input-suffix">
107
110
  {suffix}
108
111
  {iconNameRight && <PktIcon className="pkt-input-suffix-icon" name={iconNameRight} />}
112
+ {shouldShowSearchIcon && (
113
+ <PktIcon className="pkt-input-suffix-icon" name="magnifying-glass-big" />
114
+ )}
109
115
  </p>
110
116
  )}
111
117
 
112
118
  {!suffix && iconNameRight && <PktIcon className="pkt-input-icon" name={iconNameRight} />}
119
+ {!suffix && shouldShowSearchIcon && (
120
+ <PktIcon className="pkt-input-icon" name="magnifying-glass-big" />
121
+ )}
113
122
  </div>
114
123
  </PktInputWrapper>
115
124
  )