mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-11 23:02:55 +01:00
510 lines
14 KiB
TypeScript
510 lines
14 KiB
TypeScript
import { Slider } from '../../components/controls/Slider';
|
|
import guid from '../../guid';
|
|
import NodeSharedPortDefinitions from '../../node-shared-port-definitions';
|
|
import { createNodeFromReactComponent } from '../../react-component-node';
|
|
import Utils from './utils';
|
|
|
|
const thumbPopout = { group: 'thumb-styles', label: 'Thumb Styles' };
|
|
const trackPopout = { group: 'track-styles', label: 'Track Styles' };
|
|
|
|
const RangeNode = {
|
|
name: 'net.noodl.controls.range',
|
|
displayNodeName: 'Slider',
|
|
docs: 'https://docs.noodl.net/nodes/ui-controls/slider',
|
|
allowChildren: false,
|
|
noodlNodeAsProp: true,
|
|
connectionPanel: {
|
|
groupPriority: [
|
|
'General',
|
|
'Style',
|
|
'Actions',
|
|
'Events',
|
|
'States',
|
|
'Mounted',
|
|
'Hover Events',
|
|
'Pointer Events',
|
|
'Focus Events'
|
|
]
|
|
},
|
|
initialize() {
|
|
this.props.attrs = {};
|
|
this.props.sizeMode = 'contentHeight';
|
|
this.props.id = 'input-' + guid();
|
|
|
|
this.props.value = this.props.min;
|
|
this._internal.outputValue = 0;
|
|
|
|
this.props._nodeId = this.id;
|
|
|
|
//this is used by the range to communicate with the node whenever the range changes value
|
|
this.props.updateOutputValue = (value: string | number) => {
|
|
value = typeof value === 'string' ? parseFloat(value) : value;
|
|
|
|
const valueChanged = this._internal.outputValue !== value;
|
|
if (valueChanged) {
|
|
this._internal.outputValue = value;
|
|
this.flagOutputDirty('value');
|
|
this._updateOutputValuePercent(value);
|
|
this.sendSignalOnOutput('onChange');
|
|
}
|
|
|
|
// On first mount, output the initial percentage.
|
|
if (!this._internal.valuePercent) {
|
|
this._updateOutputValuePercent(value);
|
|
}
|
|
};
|
|
|
|
this.props.updateOutputValue(this.props.value); // Set the value output to an initial value
|
|
},
|
|
getReactComponent() {
|
|
return Slider;
|
|
},
|
|
inputs: {
|
|
value: {
|
|
type: 'string',
|
|
displayName: 'Value',
|
|
group: 'General',
|
|
index: 100,
|
|
set(value) {
|
|
this._setInputValue(value);
|
|
}
|
|
},
|
|
testId: {
|
|
index: 100009,
|
|
displayName: 'Test ID Attribute',
|
|
group: 'Advanced HTML',
|
|
type: 'string',
|
|
set(value) {
|
|
this.props.attrs["data-testid"] = value;
|
|
this.forceUpdate();
|
|
}
|
|
}
|
|
},
|
|
outputs: {
|
|
value: {
|
|
type: 'number',
|
|
displayName: 'Value',
|
|
group: 'States',
|
|
get() {
|
|
return this._internal.outputValue;
|
|
}
|
|
},
|
|
valuePercent: {
|
|
type: 'number',
|
|
displayName: 'Value Percent',
|
|
group: 'States',
|
|
get() {
|
|
return this._internal.valuePercent;
|
|
}
|
|
},
|
|
onChange: {
|
|
type: 'signal',
|
|
displayName: 'Changed',
|
|
group: 'Events'
|
|
}
|
|
},
|
|
inputProps: {
|
|
min: {
|
|
type: 'number',
|
|
displayName: 'Min',
|
|
group: 'General',
|
|
default: 0,
|
|
index: 100,
|
|
onChange() {
|
|
this._setInputValue(this.props.value);
|
|
}
|
|
},
|
|
max: {
|
|
type: 'number',
|
|
displayName: 'Max',
|
|
group: 'General',
|
|
default: 100,
|
|
index: 100,
|
|
onChange() {
|
|
this._setInputValue(this.props.value);
|
|
}
|
|
},
|
|
step: {
|
|
type: 'number',
|
|
displayName: 'Step',
|
|
group: 'General',
|
|
default: 1,
|
|
index: 100
|
|
},
|
|
width: {
|
|
index: 11,
|
|
group: 'Dimensions',
|
|
displayName: 'Width',
|
|
type: {
|
|
name: 'number',
|
|
units: ['%', 'px', 'vw'],
|
|
defaultUnit: '%'
|
|
},
|
|
default: 100,
|
|
allowVisualStates: true
|
|
},
|
|
// Styles
|
|
thumbWidth: {
|
|
group: 'Thumb Style',
|
|
displayName: 'Width',
|
|
type: {
|
|
name: 'number',
|
|
units: ['px', 'vw', '%'],
|
|
defaultUnit: 'px',
|
|
allowEditOnly: true
|
|
},
|
|
default: 16,
|
|
popout: thumbPopout,
|
|
allowVisualStates: true
|
|
},
|
|
thumbHeight: {
|
|
group: 'Thumb Style',
|
|
displayName: 'Height',
|
|
type: {
|
|
name: 'number',
|
|
units: ['px', 'vh', '%'],
|
|
defaultUnit: 'px',
|
|
allowEditOnly: true
|
|
},
|
|
default: 16,
|
|
popout: thumbPopout,
|
|
allowVisualStates: true
|
|
},
|
|
thumbColor: {
|
|
group: 'Thumb Style',
|
|
displayName: 'Color',
|
|
type: { name: 'color', allowEditOnly: true },
|
|
default: '#000000',
|
|
popout: thumbPopout,
|
|
allowVisualStates: true
|
|
},
|
|
trackHeight: {
|
|
group: 'Track Style',
|
|
displayName: 'Height',
|
|
type: {
|
|
name: 'number',
|
|
units: ['px', 'vh', '%'],
|
|
defaultUnit: 'px',
|
|
allowEditOnly: true
|
|
},
|
|
default: 6,
|
|
popout: trackPopout,
|
|
allowVisualStates: true
|
|
},
|
|
trackColor: {
|
|
group: 'Track Style',
|
|
displayName: 'Inactive Color',
|
|
type: { name: 'color', allowEditOnly: true },
|
|
default: '#f0f0f0',
|
|
popout: trackPopout,
|
|
allowVisualStates: true
|
|
},
|
|
trackActiveColor: {
|
|
group: 'Track Style',
|
|
displayName: 'Active Color',
|
|
type: { name: 'color', allowEditOnly: true },
|
|
default: '#f0f0f0',
|
|
popout: trackPopout,
|
|
allowVisualStates: true
|
|
}
|
|
},
|
|
methods: {
|
|
_updateOutputValuePercent(value: number) {
|
|
const min = this.props.min;
|
|
const max = this.props.max;
|
|
const valuePercent = Math.floor(((value - min) / (max - min)) * 100);
|
|
const valuePercentChanged = this._internal.valuePercentChanged !== valuePercent;
|
|
|
|
this._internal.valuePercent = valuePercent;
|
|
valuePercentChanged && this.flagOutputDirty('valuePercent');
|
|
},
|
|
_setInputValue(newValue) {
|
|
//make sure value never goes out of range
|
|
const value = Math.max(this.props.min, Math.min(this.props.max, newValue || 0));
|
|
|
|
const changed = value !== this.props.value;
|
|
|
|
if (changed) {
|
|
this.props.value = value;
|
|
this.forceUpdate();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//Add borders
|
|
function addBorderInputs(definition, opts) {
|
|
opts = opts || {};
|
|
const defaults = opts.defaults || {};
|
|
const popout = opts.popout;
|
|
|
|
defaults.borderStyle = 'none';
|
|
defaults.borderWidth = 0;
|
|
defaults.borderColor = '#000000';
|
|
|
|
const prefixLabel = opts.propPrefix[0].toUpperCase() + opts.propPrefix.slice(1);
|
|
|
|
function defineBorderTab(definition, suffix, tabName, indexOffset) {
|
|
const styleName = opts.propPrefix + `Border${suffix}Style`;
|
|
const widthName = opts.propPrefix + `Border${suffix}Width`;
|
|
const colorName = opts.propPrefix + `Border${suffix}Color`;
|
|
|
|
let orNotSet = defaults.borderStyle !== 'none' ? 'OR borderStyle NOT SET' : '';
|
|
|
|
if (suffix) {
|
|
if (defaults[styleName] && defaults[styleName] !== 'none') orNotSet += `OR ${styleName} NOT SET`;
|
|
NodeSharedPortDefinitions.addDynamicInputPorts(
|
|
definition,
|
|
`${styleName} = solid OR ${styleName} = dashed OR ${styleName} = dotted OR borderStyle = solid OR borderStyle = dashed OR borderStyle = dotted ${orNotSet}`,
|
|
[`${widthName}`, `${colorName}`]
|
|
);
|
|
} else {
|
|
NodeSharedPortDefinitions.addDynamicInputPorts(
|
|
definition,
|
|
`${styleName} = solid OR ${styleName} = dashed OR ${styleName} = dotted ${orNotSet}`,
|
|
[`${widthName}`, `${colorName}`]
|
|
);
|
|
}
|
|
|
|
const tab = {
|
|
group: opts.propPrefix + '-border-styles',
|
|
tab: tabName,
|
|
label: suffix
|
|
};
|
|
|
|
const editorName = (name) => `${prefixLabel} ${name} ${suffix ? '(' + suffix + ')' : ''}`;
|
|
const index = 202 + indexOffset * 4;
|
|
|
|
const groupName = prefixLabel + ' Border Style';
|
|
|
|
NodeSharedPortDefinitions.addInputProps(definition, {
|
|
[styleName]: {
|
|
index: index + 1,
|
|
displayName: 'Border Style',
|
|
editorName: editorName('Border Style'),
|
|
group: groupName,
|
|
type: {
|
|
name: 'enum',
|
|
enums: [
|
|
{ label: 'None', value: 'none' },
|
|
{ label: 'Solid', value: 'solid' },
|
|
{ label: 'Dotted', value: 'dotted' },
|
|
{ label: 'Dashed', value: 'dashed' }
|
|
]
|
|
},
|
|
default: defaults[`border${suffix}Style`],
|
|
tab,
|
|
popout,
|
|
allowVisualStates: true
|
|
},
|
|
[widthName]: {
|
|
index: index + 2,
|
|
displayName: 'Border Width',
|
|
editorName: editorName('Border Width'),
|
|
group: groupName,
|
|
type: {
|
|
name: 'number',
|
|
units: ['px'],
|
|
defaultUnit: 'px'
|
|
},
|
|
default: defaults[`border${suffix}Width`],
|
|
tab,
|
|
popout,
|
|
allowVisualStates: true
|
|
},
|
|
[colorName]: {
|
|
index: index + 3,
|
|
displayName: 'Border Color',
|
|
editorName: editorName('Border Color'),
|
|
group: groupName,
|
|
type: 'color',
|
|
default: defaults[`border${suffix}Color`],
|
|
tab,
|
|
popout,
|
|
allowVisualStates: true
|
|
}
|
|
});
|
|
}
|
|
|
|
defineBorderTab(definition, '', 'borders-all', 0);
|
|
defineBorderTab(definition, 'Left', 'borders-left', 1);
|
|
defineBorderTab(definition, 'Top', 'borders-top', 2);
|
|
defineBorderTab(definition, 'Right', 'borders-right', 3);
|
|
defineBorderTab(definition, 'Bottom', 'borders-bottom', 4);
|
|
}
|
|
|
|
function addBorderRadius(definition, opts) {
|
|
opts = opts || {};
|
|
const defaults = opts.defaults || {};
|
|
const popout = opts.popout;
|
|
|
|
if (!defaults.borderRadius) defaults.borderRadius = 0;
|
|
|
|
const prefixLabel = opts.propPrefix[0].toUpperCase() + opts.propPrefix.slice(1);
|
|
|
|
function defineCornerTab(definition, suffix, tabName, indexOffset) {
|
|
const editorName = (name) => `${prefixLabel} ${name} ${suffix ? '(' + suffix + ')' : ''}`;
|
|
|
|
const tab = {
|
|
group: opts.propPrefix + '-corners',
|
|
tab: tabName,
|
|
label: suffix
|
|
};
|
|
const radiusName = `Border${suffix}Radius`;
|
|
|
|
NodeSharedPortDefinitions.addInputProps(definition, {
|
|
[opts.propPrefix + radiusName]: {
|
|
index: 240 + indexOffset,
|
|
displayName: 'Corner Radius',
|
|
editorName: editorName('Corner Radius'),
|
|
group: prefixLabel + ' Corner Radius',
|
|
type: {
|
|
name: 'number',
|
|
units: ['px', '%'],
|
|
defaultUnit: 'px'
|
|
},
|
|
default: defaults[`border${suffix}Radius`],
|
|
tab,
|
|
popout
|
|
}
|
|
});
|
|
}
|
|
|
|
defineCornerTab(definition, '', 'corners-all', 0);
|
|
defineCornerTab(definition, 'TopLeft', 'corners-top-left', 1);
|
|
defineCornerTab(definition, 'TopRight', 'corners-top-right', 2);
|
|
defineCornerTab(definition, 'BottomRight', 'corners-bottom-right', 3);
|
|
defineCornerTab(definition, 'BottomLeft', 'corners-bottom-left', 4);
|
|
}
|
|
|
|
function addShadowInputs(definition, opts) {
|
|
opts = opts || {};
|
|
const popout = opts.popout;
|
|
const prefix = opts.propPrefix;
|
|
|
|
NodeSharedPortDefinitions.addDynamicInputPorts(definition, `${prefix}BoxShadowEnabled = true`, [
|
|
`${prefix}BoxShadowOffsetX`,
|
|
`${prefix}BoxShadowOffsetY`,
|
|
`${prefix}BoxShadowInset`,
|
|
`${prefix}BoxShadowBlurRadius`,
|
|
`${prefix}BoxShadowSpreadRadius`,
|
|
`${prefix}BoxShadowColor`
|
|
]);
|
|
|
|
const prefixLabel = opts.propPrefix[0].toUpperCase() + opts.propPrefix.slice(1);
|
|
const editorName = (name) => `${prefixLabel} ${name}`;
|
|
|
|
NodeSharedPortDefinitions.addInputProps(definition, {
|
|
[`${prefix}BoxShadowEnabled`]: {
|
|
index: 250,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Shadow Enabled',
|
|
editorName: editorName('Shadow Enabled'),
|
|
type: 'boolean',
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowOffsetX`]: {
|
|
index: 251,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Offset X',
|
|
editorName: editorName('Offset X'),
|
|
default: 0,
|
|
type: {
|
|
name: 'number',
|
|
units: ['px'],
|
|
defaultUnit: 'px'
|
|
},
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowOffsetY`]: {
|
|
index: 252,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Offset Y',
|
|
editorName: editorName('Offset Y'),
|
|
default: 0,
|
|
type: {
|
|
name: 'number',
|
|
units: ['px'],
|
|
defaultUnit: 'px'
|
|
},
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowBlurRadius`]: {
|
|
index: 253,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Blur Radius',
|
|
editorName: editorName('Blur Radius'),
|
|
default: 5,
|
|
type: {
|
|
name: 'number',
|
|
units: ['px'],
|
|
defaultUnit: 'px'
|
|
},
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowSpreadRadius`]: {
|
|
index: 254,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Spread Radius',
|
|
editorName: editorName('Spread Radius'),
|
|
default: 2,
|
|
type: {
|
|
name: 'number',
|
|
units: ['px'],
|
|
defaultUnit: 'px'
|
|
},
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowInset`]: {
|
|
index: 255,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Inset',
|
|
editorName: editorName('Inset'),
|
|
type: 'boolean',
|
|
default: false,
|
|
allowVisualStates: true,
|
|
popout
|
|
},
|
|
[`${prefix}BoxShadowColor`]: {
|
|
index: 256,
|
|
group: opts.group || 'Box Shadow',
|
|
displayName: 'Shadow Color',
|
|
editorName: editorName('Shadow Color'),
|
|
type: 'color',
|
|
default: '#00000033',
|
|
allowVisualStates: true,
|
|
popout
|
|
}
|
|
});
|
|
}
|
|
|
|
NodeSharedPortDefinitions.addAlignInputs(RangeNode);
|
|
NodeSharedPortDefinitions.addTransformInputs(RangeNode);
|
|
NodeSharedPortDefinitions.addMarginInputs(RangeNode);
|
|
NodeSharedPortDefinitions.addPaddingInputs(RangeNode);
|
|
NodeSharedPortDefinitions.addSharedVisualInputs(RangeNode);
|
|
addBorderInputs(RangeNode, { propPrefix: 'track', popout: trackPopout });
|
|
addBorderRadius(RangeNode, { propPrefix: 'track', popout: trackPopout });
|
|
addShadowInputs(RangeNode, {
|
|
propPrefix: 'track',
|
|
popout: trackPopout,
|
|
group: 'Track Box Shadow'
|
|
});
|
|
|
|
addBorderInputs(RangeNode, { propPrefix: 'thumb', popout: thumbPopout });
|
|
addBorderRadius(RangeNode, { propPrefix: 'thumb', popout: thumbPopout });
|
|
addShadowInputs(RangeNode, {
|
|
propPrefix: 'thumb',
|
|
popout: thumbPopout,
|
|
group: 'Thumb Box Shadow'
|
|
});
|
|
|
|
Utils.addControlEventsAndStates(RangeNode);
|
|
|
|
export default createNodeFromReactComponent(RangeNode);
|