@nordicsemiconductor/pc-nrfconnect-shared 157.0.0 → 159.0.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 (45) hide show
  1. package/Changelog.md +49 -0
  2. package/nrfutil/device/deviceInfo.ts +2 -0
  3. package/nrfutil/device/getProtectionStatus.ts +1 -7
  4. package/package.json +1 -1
  5. package/src/App/App.tsx +2 -0
  6. package/src/ConfirmBeforeClose/ConfirmCloseDialog.tsx +85 -0
  7. package/src/ConfirmBeforeClose/confirmBeforeCloseSlice.ts +96 -0
  8. package/src/Device/deviceSetup.ts +13 -1
  9. package/src/Device/deviceSlice.ts +1 -12
  10. package/src/Device/jprogOperations.ts +40 -66
  11. package/src/Dropdown/Dropdown.tsx +19 -4
  12. package/src/InlineInput/InlineInput.tsx +3 -0
  13. package/src/InlineInput/NumberInlineInput.tsx +31 -6
  14. package/src/NumberInput/NumberInput.tsx +133 -0
  15. package/src/StateSelector/state-selector.scss +0 -1
  16. package/src/index.ts +7 -1
  17. package/src/store.ts +2 -0
  18. package/typings/generated/nrfutil/device/deviceInfo.d.ts +2 -1
  19. package/typings/generated/nrfutil/device/deviceInfo.d.ts.map +1 -1
  20. package/typings/generated/nrfutil/device/getProtectionStatus.d.ts +1 -2
  21. package/typings/generated/nrfutil/device/getProtectionStatus.d.ts.map +1 -1
  22. package/typings/generated/src/App/App.d.ts.map +1 -1
  23. package/typings/generated/src/ConfirmBeforeClose/ConfirmCloseDialog.d.ts +4 -0
  24. package/typings/generated/src/ConfirmBeforeClose/ConfirmCloseDialog.d.ts.map +1 -0
  25. package/typings/generated/src/ConfirmBeforeClose/confirmBeforeCloseSlice.d.ts +16 -0
  26. package/typings/generated/src/ConfirmBeforeClose/confirmBeforeCloseSlice.d.ts.map +1 -0
  27. package/typings/generated/src/Device/deviceSetup.d.ts.map +1 -1
  28. package/typings/generated/src/Device/deviceSlice.d.ts +2 -3
  29. package/typings/generated/src/Device/deviceSlice.d.ts.map +1 -1
  30. package/typings/generated/src/Device/jprogOperations.d.ts.map +1 -1
  31. package/typings/generated/src/Dropdown/Dropdown.d.ts +3 -1
  32. package/typings/generated/src/Dropdown/Dropdown.d.ts.map +1 -1
  33. package/typings/generated/src/InlineInput/InlineInput.d.ts +1 -0
  34. package/typings/generated/src/InlineInput/InlineInput.d.ts.map +1 -1
  35. package/typings/generated/src/InlineInput/NumberInlineInput.d.ts +2 -1
  36. package/typings/generated/src/InlineInput/NumberInlineInput.d.ts.map +1 -1
  37. package/typings/generated/src/NumberInput/NumberInput.d.ts +23 -0
  38. package/typings/generated/src/NumberInput/NumberInput.d.ts.map +1 -0
  39. package/typings/generated/src/index.d.ts +2 -1
  40. package/typings/generated/src/index.d.ts.map +1 -1
  41. package/typings/generated/src/store.d.ts +5 -0
  42. package/typings/generated/src/store.d.ts.map +1 -1
  43. package/src/NumberInputWithSlider/NumberInputSliderWithUnit.tsx +0 -74
  44. package/typings/generated/src/NumberInputWithSlider/NumberInputSliderWithUnit.d.ts +0 -14
  45. package/typings/generated/src/NumberInputWithSlider/NumberInputSliderWithUnit.d.ts.map +0 -1
package/Changelog.md CHANGED
@@ -7,6 +7,55 @@ This project does _not_ adhere to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html) but contrary to it
8
8
  every new version is a new major version.
9
9
 
10
+ ## 159.0.0 - 2024-02-23
11
+
12
+ ### Added
13
+
14
+ - `minWidth` parameter to `Dropdown` component.
15
+ - `transparentButtonBg` parameter to `Dropdown` component.
16
+ - `NumberInput` component (provides text, input, optional unit, and slider).
17
+ - Common way to queue ongoing pending tasks. If an app is closed, a dialog is
18
+ prompted to alert users before clo sing app. Redux states for this are:
19
+ - `addConfirmBeforeClose`
20
+ - `clearConfirmBeforeClose`
21
+ - `preventAppCloseUntilComplete` can be used to wrap some promise and
22
+ secure app from closing until promise is resolved
23
+
24
+ ### Removed
25
+
26
+ - `NumberInputWithSlider` component.
27
+
28
+ ### Changed
29
+
30
+ - `StateSelector` no longer has 16px margin on the bottom. Apps are now
31
+ responsible to add the appropriate gap per container
32
+
33
+ ### Steps to upgrade
34
+
35
+ - Change all occurrences of `NumberInputWithSlider` to `NumberInput`.
36
+ - Check all use cases of `StateSelector` and that the gap between components
37
+ is correct if not adjust spacing from the app side
38
+
39
+ ## 158.0.0 - 2024-02-22
40
+
41
+ ### Added
42
+
43
+ - `NrfutilDeviceLib.deviceInfo` now has `protectionStatus` property.
44
+
45
+ ### Removed
46
+
47
+ - `DeviceSlice` no longer has readbackProtection state
48
+ - `DeviceSlice` no longer has setReadbackProtection has be removed
49
+
50
+ ### Changed
51
+
52
+ - `getReadbackProtection` now returns protection type `ProtectionStatus` from
53
+ nrfutil device common.ts
54
+
55
+ ### Steps to upgrade when using this package
56
+
57
+ - Minimum support version of nrfutil device is now 2.1.1
58
+
10
59
  ## 157.0.0 - 2024-02-06
11
60
 
12
61
  ### Removed
@@ -9,6 +9,7 @@ import {
9
9
  DeviceCore,
10
10
  deviceSingleTaskEndOperation,
11
11
  NrfutilDevice,
12
+ ProtectionStatus,
12
13
  } from './common';
13
14
 
14
15
  export interface HwInfo {
@@ -25,6 +26,7 @@ export interface JLinkDeviceInfo {
25
26
  jlinkObFirmwareVersion: string;
26
27
  deviceFamily: string | null;
27
28
  deviceVersion?: string | null;
29
+ protectionStatus: ProtectionStatus;
28
30
  }
29
31
 
30
32
  export interface DfuTriggerInfo {
@@ -9,6 +9,7 @@ import {
9
9
  DeviceCore,
10
10
  deviceSingleTaskEndOperation,
11
11
  NrfutilDevice,
12
+ ProtectionStatus,
12
13
  } from './common';
13
14
 
14
15
  type DeviceFamily =
@@ -17,13 +18,6 @@ type DeviceFamily =
17
18
  | 'NRF53_FAMILY'
18
19
  | 'NRF91_FAMILY';
19
20
 
20
- type ProtectionStatus =
21
- | 'NRFDL_PROTECTION_STATUS_NONE'
22
- | 'NRFDL_PROTECTION_STATUS_REGION0'
23
- | 'NRFDL_PROTECTION_STATUS_REGION0_REGION1'
24
- | 'NRFDL_PROTECTION_STATUS_SECURE_REGIONS'
25
- | 'NRFDL_PROTECTION_STATUS_ALL';
26
-
27
21
  export interface GetProtectionStatusResult {
28
22
  core: DeviceCore;
29
23
  deviceFamily?: DeviceFamily;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordicsemiconductor/pc-nrfconnect-shared",
3
- "version": "157.0.0",
3
+ "version": "159.0.0",
4
4
  "description": "Shared commodities for developing pc-nrfconnect-* packages",
5
5
  "repository": {
6
6
  "type": "git",
package/src/App/App.tsx CHANGED
@@ -15,6 +15,7 @@ import { Reducer } from 'redux';
15
15
  import { inMain as openWindow } from '../../ipc/openWindow';
16
16
  import { setNrfutilLogger } from '../../nrfutil/nrfutilLogger';
17
17
  import About from '../About/About';
18
+ import ConfirmCloseDialog from '../ConfirmBeforeClose/ConfirmCloseDialog';
18
19
  import BrokenDeviceDialog from '../Device/BrokenDeviceDialog/BrokenDeviceDialog';
19
20
  import { setAutoReselect } from '../Device/deviceAutoSelectSlice';
20
21
  import {
@@ -181,6 +182,7 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
181
182
 
182
183
  <ErrorDialog />
183
184
  <BrokenDeviceDialog />
185
+ <ConfirmCloseDialog />
184
186
  {children}
185
187
  </div>
186
188
  );
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright (c) 2015 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import React, { useEffect, useState } from 'react';
8
+ import { useDispatch, useSelector } from 'react-redux';
9
+ import { getCurrentWindow } from '@electron/remote';
10
+
11
+ import { ConfirmationDialog } from '../Dialog/Dialog';
12
+ import { AppThunk } from '../store';
13
+ import {
14
+ addConfirmBeforeClose,
15
+ clearConfirmBeforeClose,
16
+ ConfirmBeforeCloseApp,
17
+ getNextConfirmDialog,
18
+ getShowConfirmCloseDialog,
19
+ setShowCloseDialog,
20
+ } from './confirmBeforeCloseSlice';
21
+
22
+ export default () => {
23
+ const dispatch = useDispatch();
24
+ const [confirmedDialogs, setConfirmedDialogs] = useState<
25
+ ConfirmBeforeCloseApp[]
26
+ >([]);
27
+
28
+ const showCloseDialog = useSelector(getShowConfirmCloseDialog);
29
+ const nextConfirmDialog = useSelector(getNextConfirmDialog);
30
+
31
+ useEffect(() => {
32
+ if (!nextConfirmDialog && showCloseDialog) {
33
+ confirmedDialogs.forEach(confirmedDialog => {
34
+ if (confirmedDialog.onClose) confirmedDialog.onClose();
35
+ });
36
+ setConfirmedDialogs([]);
37
+ getCurrentWindow().close();
38
+ }
39
+ }, [nextConfirmDialog, dispatch, showCloseDialog, confirmedDialogs]);
40
+
41
+ useEffect(() => {
42
+ const action = (ev: BeforeUnloadEvent) =>
43
+ dispatch<AppThunk>((_, getState) => {
44
+ const hasToGetExplicitConform =
45
+ getState().confirmBeforeCloseDialog.confirmCloseApp.length >
46
+ 0;
47
+ if (hasToGetExplicitConform) {
48
+ dispatch(setShowCloseDialog(true));
49
+ ev.returnValue = true;
50
+ }
51
+ });
52
+
53
+ window.addEventListener('beforeunload', action, true);
54
+
55
+ return () => {
56
+ window.removeEventListener('beforeunload', action);
57
+ };
58
+ }, [dispatch]);
59
+
60
+ return (
61
+ <ConfirmationDialog
62
+ headerIcon="alert-outline"
63
+ title="Closing nPM PowerUP"
64
+ isVisible={showCloseDialog && !!nextConfirmDialog}
65
+ onConfirm={() => {
66
+ if (nextConfirmDialog) {
67
+ setConfirmedDialogs([
68
+ ...confirmedDialogs,
69
+ nextConfirmDialog,
70
+ ]);
71
+ dispatch(clearConfirmBeforeClose(nextConfirmDialog.id));
72
+ }
73
+ }}
74
+ onCancel={() => {
75
+ dispatch(setShowCloseDialog(false));
76
+ confirmedDialogs.forEach(confirmedDialog =>
77
+ dispatch(addConfirmBeforeClose(confirmedDialog))
78
+ );
79
+ setConfirmedDialogs([]);
80
+ }}
81
+ >
82
+ {nextConfirmDialog?.message}
83
+ </ConfirmationDialog>
84
+ );
85
+ };
@@ -0,0 +1,96 @@
1
+ /*
2
+ * Copyright (c) 2024 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
8
+ import { v4 as uuid } from 'uuid';
9
+
10
+ import type { AppThunk, RootState } from '../store';
11
+
12
+ export interface ConfirmBeforeCloseApp {
13
+ id: string;
14
+ message: React.ReactNode;
15
+ onClose?: () => void;
16
+ }
17
+
18
+ export interface ConfirmBeforeCloseState {
19
+ confirmCloseApp: ConfirmBeforeCloseApp[];
20
+ showCloseDialog: boolean;
21
+ }
22
+
23
+ const initialState: ConfirmBeforeCloseState = {
24
+ confirmCloseApp: [],
25
+ showCloseDialog: false,
26
+ };
27
+
28
+ const slice = createSlice({
29
+ name: 'confirmBeforeCloseDialog',
30
+ initialState,
31
+ reducers: {
32
+ addConfirmBeforeClose(
33
+ state,
34
+ action: PayloadAction<ConfirmBeforeCloseApp>
35
+ ) {
36
+ const index = state.confirmCloseApp.findIndex(
37
+ confirmCloseApp => confirmCloseApp.id === action.payload.id
38
+ );
39
+
40
+ if (index !== -1) {
41
+ state.confirmCloseApp[index] = action.payload;
42
+ } else {
43
+ state.confirmCloseApp = [
44
+ action.payload,
45
+ ...state.confirmCloseApp,
46
+ ];
47
+ }
48
+ },
49
+ clearConfirmBeforeClose(state, action: PayloadAction<string>) {
50
+ state.confirmCloseApp = state.confirmCloseApp.filter(
51
+ confirmCloseApp => confirmCloseApp.id !== action.payload
52
+ );
53
+ },
54
+ setShowCloseDialog(state, action: PayloadAction<boolean>) {
55
+ state.showCloseDialog = action.payload;
56
+ },
57
+ },
58
+ });
59
+
60
+ export const {
61
+ reducer,
62
+ actions: {
63
+ addConfirmBeforeClose,
64
+ setShowCloseDialog,
65
+ clearConfirmBeforeClose,
66
+ },
67
+ } = slice;
68
+
69
+ export const getNextConfirmDialog = (state: RootState) =>
70
+ state.confirmBeforeCloseDialog.confirmCloseApp.length > 0
71
+ ? state.confirmBeforeCloseDialog.confirmCloseApp[0]
72
+ : undefined;
73
+
74
+ export const getShowConfirmCloseDialog = (state: RootState) =>
75
+ state.confirmBeforeCloseDialog.showCloseDialog;
76
+
77
+ export const preventAppCloseUntilComplete =
78
+ (
79
+ dialogInfo: Omit<ConfirmBeforeCloseApp, 'id'>,
80
+ promise: Promise<unknown>,
81
+ abortController?: AbortController
82
+ ): AppThunk =>
83
+ dispatch => {
84
+ const id = uuid();
85
+ dispatch(
86
+ addConfirmBeforeClose({
87
+ ...dialogInfo,
88
+ id,
89
+ onClose: () => {
90
+ dialogInfo.onClose?.();
91
+ abortController?.abort();
92
+ },
93
+ })
94
+ );
95
+ promise.finally(() => dispatch(clearConfirmBeforeClose(id)));
96
+ };
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
  import { DeviceInfo } from '../../nrfutil/device/deviceInfo';
7
+ import { preventAppCloseUntilComplete } from '../ConfirmBeforeClose/confirmBeforeCloseSlice';
7
8
  import logger from '../logging';
8
9
  import describeError from '../logging/describeError';
9
10
  import { AppThunk, RootState } from '../store';
@@ -151,7 +152,7 @@ export const prepareDevice =
151
152
  if (!selectedDeviceSetup) {
152
153
  onFail('No firmware was selected'); // Should never happen
153
154
  } else {
154
- dispatch(
155
+ const task = dispatch(
155
156
  selectedDeviceSetup.programDevice(
156
157
  (progress: number, message?: string) => {
157
158
  dispatch(setDeviceSetupProgress(progress));
@@ -164,6 +165,17 @@ export const prepareDevice =
164
165
  )
165
166
  .then(onSuccessWrapper)
166
167
  .catch(onFail);
168
+
169
+ dispatch(
170
+ preventAppCloseUntilComplete(
171
+ {
172
+ message: `The device is being programmed.
173
+ Closing application right now might result in some unknown behavior and might also brick the device.
174
+ Are you sure you want to continue?`,
175
+ },
176
+ task
177
+ )
178
+ );
167
179
  }
168
180
  };
169
181
 
@@ -61,14 +61,12 @@ const updateDevice = (
61
61
 
62
62
  export interface DeviceState {
63
63
  devices: Device[];
64
- readbackProtection: 'unknown' | 'protected' | 'unprotected';
65
64
  selectedDevice?: Device;
66
65
  selectedDeviceInfo?: DeviceInfo;
67
66
  }
68
67
 
69
68
  const initialState: DeviceState = {
70
69
  devices: [],
71
- readbackProtection: 'unknown',
72
70
  };
73
71
 
74
72
  const setPersistedData = (device: Device) => {
@@ -124,7 +122,6 @@ const slice = createSlice({
124
122
  deselectDevice: state => {
125
123
  state.selectedDevice = undefined;
126
124
  state.selectedDeviceInfo = undefined;
127
- state.readbackProtection = 'unknown';
128
125
  },
129
126
 
130
127
  addDevice: (state, action: PayloadAction<Device>) => {
@@ -261,13 +258,6 @@ const slice = createSlice({
261
258
  action.payload.serialNumber
262
259
  );
263
260
  },
264
-
265
- setReadbackProtected: (
266
- state,
267
- action: PayloadAction<DeviceState['readbackProtection']>
268
- ) => {
269
- state.readbackProtection = action.payload;
270
- },
271
261
  },
272
262
  });
273
263
 
@@ -282,7 +272,6 @@ export const {
282
272
  removeDevice,
283
273
  setDeviceNickname,
284
274
  toggleDeviceFavorited,
285
- setReadbackProtected,
286
275
  persistSerialPortOptions,
287
276
  },
288
277
  } = slice;
@@ -304,4 +293,4 @@ export const selectedSerialNumber = (state: RootState) =>
304
293
  state.device.selectedDevice?.serialNumber;
305
294
 
306
295
  export const getReadbackProtection = (state: RootState) =>
307
- state.device.readbackProtection;
296
+ state.device.selectedDeviceInfo?.jlink?.protectionStatus;
@@ -6,42 +6,14 @@
6
6
 
7
7
  import NrfutilDeviceLib from '../../nrfutil/device/device';
8
8
  import { DeviceInfo } from '../../nrfutil/device/deviceInfo';
9
- import { FWInfo } from '../../nrfutil/device/getFwInfo';
10
9
  import logger from '../logging';
11
10
  import type { AppThunk, RootState } from '../store';
12
11
  import { DeviceSetup, JprogEntry } from './deviceSetup';
13
- import { Device, setReadbackProtected } from './deviceSlice';
14
-
15
- const getDeviceReadProtection = async (
16
- device: Device
17
- ): Promise<{
18
- fwInfo: FWInfo | null;
19
- readbackProtection: 'unknown' | 'protected' | 'unprotected';
20
- }> => {
21
- try {
22
- logger.info('Checking readback protection on device');
23
- const info = await NrfutilDeviceLib.getFwInfo(device);
24
- return {
25
- fwInfo: info,
26
- readbackProtection: 'unprotected',
27
- };
28
- } catch (e) {
29
- const error = e as Error;
30
- if (error.message.includes('NotAvailableBecauseProtection')) {
31
- logger.warn(
32
- 'Readback protection on device enabled. Unable to verify that the firmware version is correct.'
33
- );
34
- return {
35
- fwInfo: null,
36
- readbackProtection: 'protected',
37
- };
38
- }
39
- return {
40
- fwInfo: null,
41
- readbackProtection: 'unknown',
42
- };
43
- }
44
- };
12
+ import {
13
+ Device,
14
+ getReadbackProtection,
15
+ setSelectedDeviceInfo,
16
+ } from './deviceSlice';
45
17
 
46
18
  const programDeviceWithFw =
47
19
  (
@@ -52,7 +24,10 @@ const programDeviceWithFw =
52
24
  async (dispatch, getState) => {
53
25
  try {
54
26
  const batch = NrfutilDeviceLib.batch();
55
- if (getState().device.readbackProtection !== 'unprotected') {
27
+ if (
28
+ getReadbackProtection(getState()) !==
29
+ 'NRFDL_PROTECTION_STATUS_NONE'
30
+ ) {
56
31
  batch.recover('Application', {
57
32
  onTaskBegin: () =>
58
33
  logger.info(`Device protected, recovering device`),
@@ -94,11 +69,10 @@ const programDeviceWithFw =
94
69
 
95
70
  await batch.run(device);
96
71
  await NrfutilDeviceLib.reset(device);
72
+
97
73
  onProgress(0, 'Updating readback protection');
98
- const { readbackProtection } = await getDeviceReadProtection(
99
- device
100
- );
101
- dispatch(setReadbackProtected(readbackProtection));
74
+ const deviceInfo = await NrfutilDeviceLib.deviceInfo(device);
75
+ dispatch(setSelectedDeviceInfo(deviceInfo));
102
76
  onProgress(0, 'Connecting to device');
103
77
  } catch (programError) {
104
78
  if (programError instanceof Error) {
@@ -150,7 +124,7 @@ export const jprogDeviceSetup = (
150
124
  programDeviceWithFw(device, firmwareOption, onProgress)
151
125
  ),
152
126
  })),
153
- isExpectedFirmware: (device, deviceInfo) => dispatch => {
127
+ isExpectedFirmware: (device, deviceInfo) => (dispatch, getState) => {
154
128
  const fwVersions = firmwareOptions(device, firmware, deviceInfo);
155
129
  if (fwVersions.length === 0) {
156
130
  return Promise.resolve({
@@ -159,40 +133,40 @@ export const jprogDeviceSetup = (
159
133
  });
160
134
  }
161
135
 
162
- return getDeviceReadProtection(device).then(
163
- ({ fwInfo: info, readbackProtection }) => {
164
- dispatch(setReadbackProtected(readbackProtection));
165
- if (info && info.imageInfoList.length > 0) {
166
- const fw = fwVersions.find(version =>
167
- info.imageInfoList.find(
168
- imageInfo =>
169
- typeof imageInfo.version === 'string' &&
170
- imageInfo.version.includes(version.fwVersion)
171
- )
172
- );
136
+ if (
137
+ getReadbackProtection(getState()) !== 'NRFDL_PROTECTION_STATUS_NONE'
138
+ ) {
139
+ logger.warn(
140
+ 'Readback protection on device enabled. Unable to verify that the firmware version is correct.'
141
+ );
173
142
 
174
- return Promise.resolve({
175
- device,
176
- validFirmware: fw !== undefined,
177
- });
178
- }
143
+ return Promise.resolve({
144
+ device,
145
+ validFirmware: hideDeviceSetupWhenProtected,
146
+ });
147
+ }
179
148
 
180
- if (
181
- hideDeviceSetupWhenProtected &&
182
- readbackProtection === 'protected'
183
- ) {
184
- return Promise.resolve({
185
- device,
186
- validFirmware: true,
187
- });
188
- }
149
+ return NrfutilDeviceLib.getFwInfo(device).then(info => {
150
+ if (info.imageInfoList.length > 0) {
151
+ const fw = fwVersions.find(version =>
152
+ info.imageInfoList.find(
153
+ imageInfo =>
154
+ typeof imageInfo.version === 'string' &&
155
+ imageInfo.version.includes(version.fwVersion)
156
+ )
157
+ );
189
158
 
190
159
  return Promise.resolve({
191
160
  device,
192
- validFirmware: false,
161
+ validFirmware: fw !== undefined,
193
162
  });
194
163
  }
195
- );
164
+
165
+ return Promise.resolve({
166
+ device,
167
+ validFirmware: false,
168
+ });
169
+ });
196
170
  },
197
171
  tryToSwitchToApplicationMode: () => () => Promise.resolve(null),
198
172
  });
@@ -22,6 +22,8 @@ export type DropdownProps<T> = {
22
22
  defaultButtonLabel?: string;
23
23
  items: DropdownItem<T>[];
24
24
  onSelect: (item: DropdownItem<T>) => void;
25
+ transparentButtonBg?: boolean;
26
+ minWidth?: boolean;
25
27
  disabled?: boolean;
26
28
  selectedItem: DropdownItem<T>;
27
29
  numItemsBeforeScroll?: number;
@@ -34,6 +36,8 @@ export default <T,>({
34
36
  defaultButtonLabel = '',
35
37
  items,
36
38
  onSelect,
39
+ transparentButtonBg = false,
40
+ minWidth = false,
37
41
  disabled = false,
38
42
  selectedItem,
39
43
  numItemsBeforeScroll = 0,
@@ -48,7 +52,11 @@ export default <T,>({
48
52
 
49
53
  return (
50
54
  <div
51
- className={`tw-preflight tw-relative tw-w-full ${className}`}
55
+ className={classNames(
56
+ 'tw-preflight tw-relative',
57
+ minWidth ? '' : 'tw-w-full',
58
+ className
59
+ )}
52
60
  onBlur={event => {
53
61
  if (!event.currentTarget.contains(event.relatedTarget)) {
54
62
  setIsActive(false);
@@ -61,7 +69,13 @@ export default <T,>({
61
69
  <button
62
70
  id={id}
63
71
  type="button"
64
- className="tw-flex tw-h-8 tw-w-full tw-items-center tw-justify-between tw-border-0 tw-bg-gray-700 tw-px-2 tw-text-white"
72
+ className={classNames(
73
+ 'tw-flex tw-items-center tw-justify-between tw-border-0',
74
+ minWidth ? '' : 'tw-w-full',
75
+ transparentButtonBg
76
+ ? 'tw-bg-transparent'
77
+ : 'tw-h-8 tw-bg-gray-700 tw-px-2 tw-text-white'
78
+ )}
65
79
  onClick={() => setIsActive(!isActive)}
66
80
  disabled={disabled}
67
81
  >
@@ -71,7 +85,7 @@ export default <T,>({
71
85
  : selectedItem.label}
72
86
  </span>
73
87
  <span
74
- className={`mdi mdi-chevron-down tw-text-lg ${classNames(
88
+ className={`mdi mdi-chevron-down tw-text-lg/none ${classNames(
75
89
  isActive && 'tw-rotate-180'
76
90
  )}`}
77
91
  />
@@ -93,8 +107,9 @@ export default <T,>({
93
107
  numItemsBeforeScroll > 0 &&
94
108
  items.length > numItemsBeforeScroll
95
109
  }
96
- className={`tw-text-while tw-absolute tw-right-0 tw-z-10 tw-w-full tw-border-t-2 tw-border-solid tw-border-gray-600 tw-bg-gray-700 tw-p-0 ${classNames(
110
+ className={`tw-text-while tw-absolute tw-z-10 tw-border-t-2 tw-border-solid tw-border-gray-600 tw-bg-gray-700 tw-p-0 ${classNames(
97
111
  styles.content,
112
+ minWidth ? '' : 'tw-right-0 tw-w-full',
98
113
  !isActive && 'tw-hidden'
99
114
  )}`}
100
115
  >
@@ -57,6 +57,7 @@ interface Props {
57
57
  onKeyboardIncrementAction?: () => string;
58
58
  onKeyboardDecrementAction?: () => string;
59
59
  className?: string;
60
+ title?: string;
60
61
  textAlignLeft?: boolean;
61
62
  onValidityChanged?: (validity: boolean) => void;
62
63
  preventDefaultInvalidStyle?: boolean;
@@ -73,6 +74,7 @@ const InlineInput = React.forwardRef<HTMLInputElement, Props>(
73
74
  onKeyboardIncrementAction = () => externalValue,
74
75
  onKeyboardDecrementAction = () => externalValue,
75
76
  className = '',
77
+ title,
76
78
  textAlignLeft = false,
77
79
  onValidityChanged,
78
80
  preventDefaultInvalidStyle,
@@ -161,6 +163,7 @@ const InlineInput = React.forwardRef<HTMLInputElement, Props>(
161
163
  <input
162
164
  ref={ref}
163
165
  type="text"
166
+ title={title}
164
167
  className={classNames(
165
168
  'inline-input',
166
169
  preventDefaultInvalidStyle