@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
368 lines • 16.9 kB
JavaScript
import { SplitViewBase, displayModeProperty, splitBehaviorProperty, preferredPrimaryColumnWidthFractionProperty, preferredSupplementaryColumnWidthFractionProperty, preferredInspectorColumnWidthFractionProperty } from './split-view-common';
import { View } from '../core/view';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
var UISplitViewControllerDelegateImpl = (function (_super) {
__extends(UISplitViewControllerDelegateImpl, _super);
function UISplitViewControllerDelegateImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
UISplitViewControllerDelegateImpl.initWithOwner = function (owner) {
var d = UISplitViewControllerDelegateImpl.new();
d._owner = owner;
return d;
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerCollapseSecondaryViewControllerOntoPrimaryViewController = function (splitViewController, secondaryViewController, primaryViewController) {
var owner = this._owner.deref();
if (owner) {
owner.onSecondaryViewCollapsed(secondaryViewController, primaryViewController);
}
return true;
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerDidCollapse = function (svc) {
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerDidExpand = function (svc) {
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerDidHideColumn = function (svc, column) {
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerDidShowColumn = function (svc, column) {
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerDisplayModeForExpandingToProposedDisplayMode = function (svc, proposedDisplayMode) {
return UISplitViewControllerDisplayMode.TwoBesideSecondary;
};
UISplitViewControllerDelegateImpl.prototype.splitViewControllerTopColumnForCollapsingToProposedTopColumn = function (svc, proposedTopColumn) {
return UISplitViewControllerColumn.Secondary;
};
UISplitViewControllerDelegateImpl.prototype.toggleInspector = function () {
var owner = this._owner.deref();
if (owner) {
if (owner.inspectorShowing) {
owner.hideInspector();
}
else {
owner.showInspector();
}
}
};
UISplitViewControllerDelegateImpl.ObjCProtocols = [UISplitViewControllerDelegate];
UISplitViewControllerDelegateImpl.ObjCExposedMethods = {
toggleInspector: { returns: interop.types.void, params: [] },
};
return UISplitViewControllerDelegateImpl;
}(NSObject));
export class SplitView extends SplitViewBase {
static getInstance() {
return SplitView.instance;
}
constructor() {
super();
// Keep role -> controller
this._controllers = new Map();
this._children = new Map();
this.inspectorShowing = false;
this.viewController = UISplitViewController.alloc().initWithStyle(this._getSplitStyle());
}
createNativeView() {
SplitView.instance = this;
this._delegate = UISplitViewControllerDelegateImpl.initWithOwner(new WeakRef(this));
this.viewController.delegate = this._delegate;
this.viewController.presentsWithGesture = true;
// Apply initial preferences
this._applyPreferences();
return this.viewController.view;
}
disposeNativeView() {
super.disposeNativeView();
this._controllers.clear();
this._children.clear();
this.viewController = null;
this._delegate = null;
}
_getSplitStyle() {
switch (SplitView.SplitStyle) {
case 'triple':
return 2 /* UISplitViewControllerStyle.TripleColumn */;
default:
// default to double always
return 1 /* UISplitViewControllerStyle.DoubleColumn */;
}
}
// Controller-backed container: intercept native tree operations
_addViewToNativeVisualTree(child, atIndex) {
const role = this._resolveRoleForChild(child, atIndex);
const controller = this._ensureControllerForChild(child);
this._children.set(role, child);
this._controllers.set(role, controller);
this._syncControllers();
return true;
}
_removeViewFromNativeVisualTree(child) {
const role = this._findRoleByChild(child);
if (role) {
this._children.delete(role);
this._controllers.delete(role);
this._syncControllers();
}
super._removeViewFromNativeVisualTree(child);
}
onMeasure(widthMeasureSpec, heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const horizontal = this.effectivePaddingLeft + this.effectivePaddingRight + this.effectiveBorderLeftWidth + this.effectiveBorderRightWidth;
const vertical = this.effectivePaddingTop + this.effectivePaddingBottom + this.effectiveBorderTopWidth + this.effectiveBorderBottomWidth;
const measuredWidth = Math.max(widthMode === layout.UNSPECIFIED ? 0 : width, this.effectiveMinWidth) + (widthMode === layout.UNSPECIFIED ? horizontal : 0);
const measuredHeight = Math.max(heightMode === layout.UNSPECIFIED ? 0 : height, this.effectiveMinHeight) + (heightMode === layout.UNSPECIFIED ? vertical : 0);
const widthAndState = View.resolveSizeAndState(measuredWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measuredHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
onRoleChanged(view, oldValue, newValue) {
// Move child mapping to new role and resync
const oldRole = this._findRoleByChild(view);
if (oldRole) {
const controller = this._controllers.get(oldRole);
this._controllers.delete(oldRole);
this._children.delete(oldRole);
if (controller) {
this._controllers.set(newValue, controller);
}
this._children.set(newValue, view);
this._syncControllers();
}
}
onSecondaryViewCollapsed(secondaryViewController, primaryViewController) {
// Default implementation: do nothing.
// Subclasses may override to customize behavior when secondary is collapsed onto primary.
}
showPrimary() {
if (!this.viewController)
return;
this.viewController.showColumn(0 /* UISplitViewControllerColumn.Primary */);
}
hidePrimary() {
if (!this.viewController)
return;
this.viewController.hideColumn(0 /* UISplitViewControllerColumn.Primary */);
}
showSecondary() {
if (!this.viewController)
return;
this.viewController.showColumn(2 /* UISplitViewControllerColumn.Secondary */);
}
hideSecondary() {
if (!this.viewController)
return;
this.viewController.hideColumn(2 /* UISplitViewControllerColumn.Secondary */);
}
showSupplementary() {
if (!this.viewController)
return;
this.viewController.showColumn(1 /* UISplitViewControllerColumn.Supplementary */);
}
showInspector() {
if (!this.viewController)
return;
// Guard for older OS versions by feature-detecting inspector-related API
if (this.viewController.preferredInspectorColumnWidthFraction !== undefined) {
this.viewController.showColumn(4 /* UISplitViewControllerColumn.Inspector */);
this.notifyInspectorChange(true);
}
}
hideInspector() {
if (!this.viewController)
return;
if (this.viewController.preferredInspectorColumnWidthFraction !== undefined) {
this.viewController.hideColumn(4 /* UISplitViewControllerColumn.Inspector */);
this.notifyInspectorChange(false);
}
}
notifyInspectorChange(showing) {
this.inspectorShowing = showing;
this.notify({
eventName: 'inspectorChange',
object: this,
data: { showing },
});
}
_resolveRoleForChild(child, atIndex) {
const explicit = SplitViewBase.getRole(child);
if (explicit) {
return explicit;
}
// Fallback by index if no explicit role set
return this._roleByIndex(atIndex);
}
_findRoleByChild(child) {
for (const [role, c] of this._children.entries()) {
if (c === child) {
return role;
}
}
return null;
}
_ensureControllerForChild(child) {
// If child is controller-backed (Page/Frame/etc.), reuse its controller
const vc = (child.ios instanceof UIViewController ? child.ios : child.viewController);
if (vc) {
return vc;
}
// Fallback: basic wrapper (not expected in current usage where children are Frames/Pages)
const wrapper = UIViewController.new();
if (!wrapper.view) {
wrapper.view = UIView.new();
}
if (child.nativeViewProtected) {
wrapper.view.addSubview(child.nativeViewProtected);
}
return wrapper;
}
_attachInspectorButton() {
const inspector = this._controllers.get('inspector');
if (!(inspector instanceof UINavigationController)) {
return;
}
const targetVC = inspector.topViewController;
if (!targetVC) {
// Subscribe to Frame event to know when the top VC is shown, then attach the button.
// Can only attach buttons once VC is available
const frameChild = this._children.get('inspector');
if (frameChild && frameChild.on && !frameChild._inspectorVCShownHandler) {
frameChild._inspectorVCShownHandler = () => {
setTimeout(() => this._attachInspectorButton());
};
frameChild.on('viewControllerShown', frameChild._inspectorVCShownHandler.bind(this));
}
return;
}
// Avoid duplicates
if (targetVC.navigationItem.rightBarButtonItem) {
return;
}
// TODO: can provide properties to customize this
const cfg = UIImageSymbolConfiguration.configurationWithPointSizeWeightScale(18, 4 /* UIImageSymbolWeight.Regular */, 2 /* UIImageSymbolScale.Medium */);
const image = UIImage.systemImageNamedWithConfiguration('sidebar.trailing', cfg);
const item = UIBarButtonItem.alloc().initWithImageStyleTargetAction(image, 0 /* UIBarButtonItemStyle.Plain */, this._delegate, 'toggleInspector');
targetVC.navigationItem.rightBarButtonItem = item;
}
_syncControllers() {
if (!this.viewController) {
return;
}
// Prefer modern API if present; otherwise fall back to setting viewControllers array
const primary = this._controllers.get('primary');
const secondary = this._controllers.get('secondary');
const supplementary = this._controllers.get('supplementary');
const inspector = this._controllers.get('inspector');
if (primary) {
this.viewController.setViewControllerForColumn(primary, 0 /* UISplitViewControllerColumn.Primary */);
}
if (secondary) {
this.viewController.setViewControllerForColumn(secondary, 2 /* UISplitViewControllerColumn.Secondary */);
}
if (supplementary) {
this.viewController.setViewControllerForColumn(supplementary, 1 /* UISplitViewControllerColumn.Supplementary */);
}
if (inspector) {
if (this.viewController.preferredInspectorColumnWidthFraction !== undefined) {
this.viewController.setViewControllerForColumn(inspector, 4 /* UISplitViewControllerColumn.Inspector */);
}
}
this._applyPreferences();
}
_applyPreferences() {
if (!this.viewController) {
return;
}
// displayMode
let preferredDisplayMode = 0 /* UISplitViewControllerDisplayMode.Automatic */;
switch (this.displayMode) {
case 'secondaryOnly':
preferredDisplayMode = 1 /* UISplitViewControllerDisplayMode.SecondaryOnly */;
break;
case 'oneBesideSecondary':
preferredDisplayMode = 2 /* UISplitViewControllerDisplayMode.OneBesideSecondary */;
break;
case 'oneOverSecondary':
preferredDisplayMode = 3 /* UISplitViewControllerDisplayMode.OneOverSecondary */;
break;
case 'twoBesideSecondary':
preferredDisplayMode = 4 /* UISplitViewControllerDisplayMode.TwoBesideSecondary */;
break;
case 'twoOverSecondary':
preferredDisplayMode = 5 /* UISplitViewControllerDisplayMode.TwoOverSecondary */;
break;
case 'twoDisplaceSecondary':
preferredDisplayMode = 6 /* UISplitViewControllerDisplayMode.TwoDisplaceSecondary */;
break;
}
this.viewController.preferredDisplayMode = preferredDisplayMode;
// splitBehavior (iOS 14+)
const sb = this.splitBehavior;
let preferredSplitBehavior = 0 /* UISplitViewControllerSplitBehavior.Automatic */;
switch (sb) {
case 'tile':
preferredSplitBehavior = 1 /* UISplitViewControllerSplitBehavior.Tile */;
break;
case 'overlay':
preferredSplitBehavior = 2 /* UISplitViewControllerSplitBehavior.Overlay */ ?? 0 /* UISplitViewControllerSplitBehavior.Automatic */;
break;
case 'displace':
preferredSplitBehavior = 3 /* UISplitViewControllerSplitBehavior.Displace */ ?? 0 /* UISplitViewControllerSplitBehavior.Automatic */;
break;
}
this.viewController.preferredSplitBehavior = preferredSplitBehavior;
const primary = this._controllers.get('primary');
const secondary = this._controllers.get('secondary');
const supplementary = this._controllers.get('supplementary');
const inspector = this._controllers.get('inspector');
if (secondary instanceof UINavigationController && secondary.navigationItem) {
// TODO: can add properties to customize this
secondary.navigationItem.leftBarButtonItem = this.viewController.displayModeButtonItem;
secondary.navigationItem.leftItemsSupplementBackButton = true;
}
if (supplementary) {
this.showSupplementary();
}
if (inspector) {
this.showInspector();
// Ensure the inspector column gets its toggle button as soon as the first page is shown
this._attachInspectorButton();
}
// Width fractions
if (typeof this.preferredPrimaryColumnWidthFraction === 'number' && !isNaN(this.preferredPrimaryColumnWidthFraction)) {
this.viewController.preferredPrimaryColumnWidthFraction = this.preferredPrimaryColumnWidthFraction;
}
if (SplitView.SplitStyle === 'triple') {
// supplementary applies in triple style
if (typeof this.preferredSupplementaryColumnWidthFraction === 'number' && !isNaN(this.preferredSupplementaryColumnWidthFraction)) {
this.viewController.preferredSupplementaryColumnWidthFraction = this.preferredSupplementaryColumnWidthFraction;
}
}
if (SDK_VERSION >= 26) {
// Inspector width fraction
const inspectorWidth = this.preferredInspectorColumnWidthFraction;
if (typeof inspectorWidth === 'number' && !isNaN(inspectorWidth)) {
this.viewController.preferredInspectorColumnWidthFraction = inspectorWidth;
}
}
}
[displayModeProperty.setNative](value) {
this._applyPreferences();
}
[splitBehaviorProperty.setNative](value) {
this._applyPreferences();
}
[preferredPrimaryColumnWidthFractionProperty.setNative](value) {
this._applyPreferences();
}
[preferredSupplementaryColumnWidthFractionProperty.setNative](value) {
this._applyPreferences();
}
[preferredInspectorColumnWidthFractionProperty.setNative](value) {
this._applyPreferences();
}
}
//# sourceMappingURL=index.ios.js.map