@octane-xplat/picker 0.13.0 → 0.15.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.
package/README.md CHANGED
@@ -12,11 +12,11 @@ There is no shared picker API at the package root. Each platform entry exports
12
12
  its own component and types; import it from the platform subpath in a matching
13
13
  platform-suffixed file:
14
14
 
15
- | File suffix | Import | Component |
16
- | --- | --- | --- |
17
- | `*.ios.tsx` | `@octane-xplat/picker/ios` | `SwiftUIPicker` |
15
+ | File suffix | Import | Component |
16
+ | --------------- | ------------------------------ | ------------------ |
17
+ | `*.ios.tsx` | `@octane-xplat/picker/ios` | `SwiftUIPicker` |
18
18
  | `*.android.tsx` | `@octane-xplat/picker/android` | `MaterialDropdown` |
19
- | `*.web.tsx` | `@octane-xplat/picker/web` | `Select` |
19
+ | `*.web.tsx` | `@octane-xplat/picker/web` | `Select` |
20
20
 
21
21
  ```tsx
22
22
  /** @jsxImportSource @nativescript-community/octane */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octane-xplat/picker",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Select control for Octane xplat — web select + SwiftUI picker (iOS) / Material dropdown (Android)",
5
5
  "keywords": [
6
6
  "android",
@@ -23,30 +23,31 @@
23
23
  "directory": "packages/picker"
24
24
  },
25
25
  "files": [
26
- "dist",
27
26
  "types",
28
27
  "src",
29
- "platforms"
28
+ "platforms",
29
+ "!src/**/*.test.*",
30
+ "!src/**/*.spec.*"
30
31
  ],
31
32
  "type": "module",
32
33
  "exports": {
33
34
  ".": {
34
35
  "types": "./types/index.d.ts",
35
- "web": "./dist/native/index.js",
36
- "native": "./dist/native/index.js",
37
- "default": "./dist/native/index.js"
36
+ "web": "./src/index.ts",
37
+ "native": "./src/index.ts",
38
+ "default": "./src/index.ts"
38
39
  },
39
40
  "./ios": {
40
41
  "types": "./types/ios/index.d.ts",
41
- "native": "./dist/native/ios/index.js"
42
+ "native": "./src/ios/index.ts"
42
43
  },
43
44
  "./android": {
44
45
  "types": "./types/android/index.d.ts",
45
- "native": "./dist/native/android/index.js"
46
+ "native": "./src/android/index.ts"
46
47
  },
47
48
  "./web": {
48
49
  "types": "./types/web/index.d.ts",
49
- "web": "./dist/web/web/index.js"
50
+ "web": "./src/web/index.ts"
50
51
  }
51
52
  },
52
53
  "publishConfig": {
@@ -57,11 +58,11 @@
57
58
  "@nativescript/swift-ui": "4.0.2"
58
59
  },
59
60
  "devDependencies": {
60
- "@nativescript-community/octane": "0.2.4",
61
+ "@nativescript-community/octane": "0.2.5",
61
62
  "@nativescript/core": "9.1.3",
62
63
  "@octanejs/vite-plugin": "0.1.61",
63
64
  "octane": "0.11.0",
64
- "tsrx-typegen": "0.2.2",
65
+ "tsrx-typegen": "0.3.0",
65
66
  "typescript": "5.9.3",
66
67
  "vite": "8.3.0"
67
68
  },
@@ -88,8 +89,8 @@
88
89
  }
89
90
  },
90
91
  "scripts": {
91
- "build": "vite build && vite build --mode native && tsc -p tsconfig.types.json && node ../../scripts/check-native-dist.mjs",
92
92
  "test:packed": "node tests/packed-consumer.mjs",
93
- "pack:check": "tsrx-typegen --pack-check"
93
+ "pack:check": "tsrx-typegen --pack-check",
94
+ "typegen": "tsc -p tsconfig.types.json"
94
95
  }
95
96
  }
@@ -65,25 +65,26 @@ public final class XplatPickerProvider: UIViewController, SwiftUIProvider {
65
65
  }
66
66
 
67
67
  public func updateData(data: NSDictionary) {
68
- if let label = data["label"] as? String { model.label = label }
69
- if let accessibilityLabel = data["accessibilityLabel"] as? String {
70
- model.accessibilityLabel = accessibilityLabel
71
- }
72
- if let value = data["value"] as? String { model.value = value }
73
- if let disabled = data["disabled"] as? NSNumber { model.disabled = disabled.boolValue }
74
- if let options = data["options"] as? [[String: Any]] {
75
- model.options = options.compactMap { option in
76
- guard let value = option["value"] as? String,
77
- let label = option["label"] as? String
78
- else { return nil }
68
+ let options = ((data["options"] as? [[String: Any]]) ?? []).compactMap { option -> PickerOption? in
69
+ guard let value = option["value"] as? String,
70
+ let label = option["label"] as? String
71
+ else { return nil }
79
72
 
80
- return PickerOption(
81
- value: value,
82
- label: label,
83
- disabled: (option["disabled"] as? NSNumber)?.boolValue ?? false,
84
- )
85
- }
73
+ return PickerOption(
74
+ value: value,
75
+ label: label,
76
+ disabled: (option["disabled"] as? NSNumber)?.boolValue ?? false,
77
+ )
86
78
  }
79
+
80
+ // Full replace — matching the Kotlin provider: absent keys reset to
81
+ // defaults and a missing value falls back to the first option, so a
82
+ // partial payload cannot leave divergent state across platforms.
83
+ model.label = data["label"] as? String ?? ""
84
+ model.accessibilityLabel = data["accessibilityLabel"] as? String ?? ""
85
+ model.value = data["value"] as? String ?? options.first?.value ?? ""
86
+ model.disabled = (data["disabled"] as? NSNumber)?.boolValue ?? false
87
+ model.options = options
87
88
  }
88
89
 
89
90
  public var onEvent: ((NSDictionary) -> Void)?
@@ -22,7 +22,9 @@ registerJetpackCompose('xplat-material-dropdown', (owner: any) =>
22
22
  export function MaterialDropdown(props: MaterialDropdownProps) @{
23
23
  const controlled = props.selectedKey !== undefined
24
24
  const [internalKey, setInternalKey] = useState(props.defaultSelectedKey ?? props.items[0]?.key ?? '')
25
- const selectedKey = controlled ? props.selectedKey! : internalKey
25
+ // Uncontrolled with a blank internal selection displays the first item
26
+ // once items arrive — matching the other leaves' auto-select-first.
27
+ const selectedKey = controlled ? props.selectedKey! : internalKey || (props.items[0]?.key ?? '')
26
28
  const data = {
27
29
  label: props.label,
28
30
  accessibilityLabel: props.accessibilityLabel ?? props.label,
@@ -6,15 +6,17 @@ import type { SelectProps } from './web/types'
6
6
  export function Select(props: SelectProps) @{
7
7
  const controlled = props.value !== undefined
8
8
  const [internalValue, setInternalValue] = useState(props.defaultValue ?? props.options[0]?.value ?? '')
9
- const value = controlled ? props.value! : internalValue
10
- const labelId = props.id ? `${props.id}-label` : undefined
9
+ // Uncontrolled with a blank internal selection displays the first option
10
+ // once options exist — the default is auto-select-first.
11
+ const value = controlled ? props.value! : internalValue || (props.options[0]?.value ?? '')
11
12
 
12
13
  <label className={props.className} style={props.style}>
13
- <span id={labelId}>{props.label}</span>
14
+ {/* The wrapping <label> supplies the implicit accessible name;
15
+ aria-label overrides it with accessibilityLabel when given. */}
16
+ <span>{props.label}</span>
14
17
  <select
15
18
  id={props.id}
16
19
  aria-label={props.accessibilityLabel ?? props.label}
17
- aria-labelledby={labelId}
18
20
  value={value}
19
21
  disabled={props.disabled}
20
22
  onChange={(event: any) => {
@@ -22,7 +22,9 @@ registerSwiftUI('xplat-swiftui-picker', (owner: any) =>
22
22
  export function SwiftUIPicker(props: SwiftUIPickerProps) @{
23
23
  const controlled = props.selection !== undefined
24
24
  const [internalSelection, setInternalSelection] = useState(props.defaultSelection ?? props.options[0]?.id ?? '')
25
- const selection = controlled ? props.selection! : internalSelection
25
+ // Uncontrolled with a blank internal selection displays the first option
26
+ // once options arrive — matching the other leaves' auto-select-first.
27
+ const selection = controlled ? props.selection! : internalSelection || (props.options[0]?.id ?? '')
26
28
  const update = (nextSelection: string) => {
27
29
  if (!controlled) setInternalSelection(nextSelection)
28
30
  props.onSelectionChange?.(nextSelection)
@@ -1,49 +0,0 @@
1
- import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalValue, useState } from "@nativescript-community/octane";
2
- import { ComposeDataDriver, JetpackCompose, registerJetpackCompose } from "@nativescript/jetpack-compose";
3
- //#region src/MaterialDropdown.android.tsrx
4
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
5
- var __octaneUniversalPlan0 = universalPlan("nativescript", {
6
- "kind": "host",
7
- "type": "xplatandroidpicker",
8
- "props": { "composeId": "xplat-material-dropdown" },
9
- "bindings": [
10
- ["id", 0],
11
- ["className", 1],
12
- ["style", 2],
13
- ["data", 3],
14
- ["onComposeEvent", 4]
15
- ]
16
- });
17
- registerElement("xplatandroidpicker", JetpackCompose);
18
- registerJetpackCompose("xplat-material-dropdown", (owner) => new ComposeDataDriver(new com.octanexplat.nativepicker.XplatPickerProvider(), owner));
19
- var MaterialDropdown = defineUniversalComponent("nativescript", function MaterialDropdown(props) {
20
- const controlled = props.selectedKey !== void 0;
21
- const [internalKey, setInternalKey] = useState(props.defaultSelectedKey ?? props.items[0]?.key ?? "", _h$0);
22
- const selectedKey = controlled ? props.selectedKey : internalKey;
23
- const data = {
24
- label: props.label,
25
- accessibilityLabel: props.accessibilityLabel ?? props.label,
26
- options: props.items.map((item) => ({
27
- value: item.key,
28
- label: item.text,
29
- disabled: item.enabled === false ? 1 : 0
30
- })),
31
- value: selectedKey,
32
- disabled: props.enabled === false ? 1 : 0
33
- };
34
- const update = (nextKey) => {
35
- if (!controlled) setInternalKey(nextKey);
36
- props.onSelectedKeyChange?.(nextKey);
37
- };
38
- return universalValue(__octaneUniversalPlan0, [
39
- props.id,
40
- props.className,
41
- props.style,
42
- data,
43
- (event) => {
44
- if (typeof event.data === "string") update(event.data);
45
- }
46
- ]);
47
- }, { "module": "@nativescript-community/octane" });
48
- //#endregion
49
- export { MaterialDropdown };
@@ -1,49 +0,0 @@
1
- import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalValue, useState } from "@nativescript-community/octane";
2
- import { SwiftUI, UIDataDriver, registerSwiftUI } from "@nativescript/swift-ui";
3
- //#region src/SwiftUIPicker.ios.tsrx
4
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
5
- var __octaneUniversalPlan0 = universalPlan("nativescript", {
6
- "kind": "host",
7
- "type": "xplatiospicker",
8
- "props": { "swiftId": "xplat-swiftui-picker" },
9
- "bindings": [
10
- ["id", 0],
11
- ["className", 1],
12
- ["style", 2],
13
- ["data", 3],
14
- ["onSwiftUIEvent", 4]
15
- ]
16
- });
17
- registerElement("xplatiospicker", SwiftUI);
18
- registerSwiftUI("xplat-swiftui-picker", (owner) => new UIDataDriver(new XplatPickerProvider(), owner));
19
- var SwiftUIPicker = defineUniversalComponent("nativescript", function SwiftUIPicker(props) {
20
- const controlled = props.selection !== void 0;
21
- const [internalSelection, setInternalSelection] = useState(props.defaultSelection ?? props.options[0]?.id ?? "", _h$0);
22
- const selection = controlled ? props.selection : internalSelection;
23
- const update = (nextSelection) => {
24
- if (!controlled) setInternalSelection(nextSelection);
25
- props.onSelectionChange?.(nextSelection);
26
- };
27
- return universalValue(__octaneUniversalPlan0, [
28
- props.id,
29
- props.className,
30
- props.style,
31
- {
32
- label: props.label,
33
- accessibilityLabel: props.accessibilityLabel ?? props.label,
34
- options: props.options.map((option) => ({
35
- value: option.id,
36
- label: option.title,
37
- disabled: option.disabled ?? false
38
- })),
39
- value: selection,
40
- disabled: props.disabled ?? false
41
- },
42
- (event) => {
43
- const nextSelection = event.data?.value;
44
- if (typeof nextSelection === "string") update(nextSelection);
45
- }
46
- ]);
47
- }, { "module": "@nativescript-community/octane" });
48
- //#endregion
49
- export { SwiftUIPicker };
@@ -1,2 +0,0 @@
1
- import { MaterialDropdown } from "../MaterialDropdown.android.tsrx.js";
2
- export { MaterialDropdown };
File without changes
@@ -1,2 +0,0 @@
1
- import { SwiftUIPicker } from "../SwiftUIPicker.ios.tsrx.js";
2
- export { SwiftUIPicker };
@@ -1,8 +0,0 @@
1
- {
2
- "version": 1,
3
- "buildId": "9b2ddcdd-119c-4b67-a265-0026cd14470a",
4
- "mode": "production",
5
- "capabilities": {
6
- "independentHydration": false
7
- }
8
- }
@@ -1,94 +0,0 @@
1
- import { __s, bag13, bag5, child, clone, createScopedElement, delegateEvents, hookSlots, mapSlot, setEventHandler, setSelectValue, setStyle, sibling, template, useState } from "octane";
2
- import { childTextHoleUpdate, compilerMemoRegion, hookMemoEqual, setAriaAttributeIfChanged, setAttributeIfChanged, setBooleanAttributeIfChanged, setClassNameIfChanged, setPlainAttributeIfChanged } from "octane/internal/client";
3
- //#region src/Select.web.tsrx
4
- delegateEvents(["change"]);
5
- var _t$0 = /* @__PURE__ */ template("<option></option>");
6
- var _t$1 = /* @__PURE__ */ template("<label><span></span><select><!></select></label>");
7
- var _hs$ = /* @__PURE__ */ hookSlots(2);
8
- var _key$1 = (option) => option.value;
9
- function __item$2(option, __s, __extra) {
10
- let _b = __s.slots[0];
11
- if (_b === void 0) {
12
- const _root = clone(_t$0);
13
- _b = bag5(__s, _root, void 0, _root, void 0, null, __s);
14
- }
15
- _b.a = setAttributeIfChanged(option.value, _b.a, _b.b, "value");
16
- _b.c = setBooleanAttributeIfChanged(option.disabled, _b.c, _b.b, "disabled");
17
- {
18
- const _v = option.label;
19
- _b.d = childTextHoleUpdate(__s, 1, _b.b, _v, _b.d, _b.e);
20
- _b.e = _v;
21
- }
22
- }
23
- var Select = /* @__PURE__ */ __s(function Select(props, __s, __extra) {
24
- const __memoRegion = compilerMemoRegion(__s, _hs$ + 0);
25
- const __memoCommitted = __memoRegion.auto;
26
- let __memoCache = __memoCommitted === void 0 ? [] : __memoCommitted;
27
- let __mapItems;
28
- let __mapMethod;
29
- const controlled = props.value !== void 0;
30
- const [internalValue, setInternalValue] = useState(props.defaultValue ?? props.options[0]?.value ?? "", _hs$ + 0);
31
- const value = controlled ? props.value : internalValue;
32
- const labelId = props.id ? `${props.id}-label` : void 0;
33
- if (__s.block.pending && !__s.block.crossRenderUpdate) return;
34
- let _b = __s.slots[0];
35
- if (_b === void 0) {
36
- let _m10;
37
- const _root = clone(_t$1);
38
- const _el0 = child(_root);
39
- const _el1 = sibling(_el0, 1);
40
- setSelectValue(_el1, value);
41
- setEventHandler(_el1, "$ochange", (event) => {
42
- const nextValue = event.target.value;
43
- if (!controlled) setInternalValue(nextValue);
44
- props.onChange?.(nextValue);
45
- });
46
- _m10 = child(_el1);
47
- _b = bag13(__s, _root, void 0, _root, void 0, void 0, _el0, void 0, _el1, void 0, void 0, void 0, _m10, null, __s);
48
- } else {
49
- setSelectValue(_b.g, value);
50
- setEventHandler(_b.g, "$ochange", (event) => {
51
- const nextValue = event.target.value;
52
- if (!controlled) setInternalValue(nextValue);
53
- props.onChange?.(nextValue);
54
- });
55
- }
56
- _b.a = setClassNameIfChanged(props.className, _b.a, _b.b);
57
- {
58
- const _v = props.style;
59
- if (_b.c !== _v) {
60
- setStyle(_b.b, _v, _b.c);
61
- _b.c = _v;
62
- }
63
- }
64
- _b.d = setPlainAttributeIfChanged(labelId, _b.d, _b.e, "id");
65
- _b.f = setPlainAttributeIfChanged(props.id, _b.f, _b.g, "id");
66
- _b.h = setAriaAttributeIfChanged(props.accessibilityLabel ?? props.label, _b.h, _b.g, "aria-label");
67
- _b.i = setAriaAttributeIfChanged(labelId, _b.i, _b.g, "aria-labelledby");
68
- _b.j = setBooleanAttributeIfChanged(props.disabled, _b.j, _b.g, "disabled");
69
- {
70
- const _v = props.label;
71
- _b.l = childTextHoleUpdate(__s, 1, _b.e, _v, _b.l, _b.m);
72
- _b.m = _v;
73
- }
74
- {
75
- const _v = (__mapItems = props.options, __mapMethod = __mapItems.map, __mapItems);
76
- const __mapNative = mapSlot(_v, __mapMethod);
77
- {
78
- const __memoDep = _v;
79
- if (__s.slots[2] === void 0 || !__mapNative || __memoCache[1] !== true || !hookMemoEqual(__memoCache[0], __memoDep)) {
80
- mapSlot(__s, 2, _b.g, _v, __mapMethod, __mapNative, (option) => createScopedElement("option", {
81
- key: option.value,
82
- value: option.value,
83
- disabled: option.disabled
84
- }, () => option.label), _key$1, __item$2, 27, void 0, _b.k, 1);
85
- if (__memoCache === __memoCommitted) __memoCache = __memoCache.slice();
86
- __memoCache[0] = __memoDep;
87
- __memoCache[1] = __mapNative;
88
- }
89
- }
90
- }
91
- if (__memoCache !== __memoCommitted) __memoRegion.auto = __memoCache;
92
- });
93
- //#endregion
94
- export { Select };
@@ -1,8 +0,0 @@
1
- {
2
- "version": 1,
3
- "buildId": "8679d795-c48b-472f-9c07-c4d34fd66238",
4
- "mode": "production",
5
- "capabilities": {
6
- "independentHydration": false
7
- }
8
- }
@@ -1,2 +0,0 @@
1
- import { Select } from "../Select.web.tsrx.js";
2
- export { Select };