added more attributes and generateCode to more widgets

This commit is contained in:
paul
2024-09-27 19:22:33 +05:30
parent 77b1c5a0f0
commit 2c7a1ada48
13 changed files with 252 additions and 218 deletions

View File

@@ -41,15 +41,6 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@@ -20,7 +20,7 @@ import { isNumeric, removeKeyFromObject } from "../../utils/common"
// FIXME: the drag drop indicator is not going invisible if the drop happens on the child
// FIXME: once the width and height is set to fit-content, it can no longer be resized
const ATTRS_KEYS = ['value', 'label', 'tool', 'onChange', 'toolProps'] // these are attrs keywords, don't use these keywords as keys while defining the attrs property
const ATTRS_KEYS = ['value', 'label', 'tool', 'onChange', 'options', 'toolProps'] // these are attrs keywords, don't use these keywords as keys while defining the attrs property or serializing
/**

View File

@@ -5,4 +5,11 @@ export const RELIEF = [
"SUNKEN",
"GROOVE",
"RIDGE"
]
export const JUSTIFY = [
"LEFT",
"CENTER",
"RIGHT"
]

View File

@@ -1,12 +1,11 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
import { CheckSquareFilled } from "@ant-design/icons"
import {TkinterBase} from "./base"
import { TkinterWidgetBase } from "./base"
export class CheckBox extends TkinterBase{
export class CheckBox extends TkinterWidgetBase{
static widgetType = "check_button"
constructor(props) {
@@ -63,12 +62,11 @@ export class CheckBox extends TkinterBase{
generateCode(variableName, parent){
const labelText = this.getAttrValue("checkLabel")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const config = convertObjectToKeyValueString(this.getConfigCode())
const code = [
`${variableName} = tk.Checkbutton(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.config(${config})`,
]
if (this.getAttrValue("defaultChecked")){
@@ -122,43 +120,22 @@ export class CheckBox extends TkinterBase{
}
export class RadioButton extends TkinterBase{
export class RadioButton extends TkinterWidgetBase{
// FIXME: the radio buttons are not visible because of the default heigh provided
static widgetType = "radio_button"
constructor(props) {
super(props)
this.droppableTags = null // disables drops
// const {layout, ...newAttrs} = this.state.attrs // Removes the layout attribute
let newAttrs = removeKeyFromObject("layout", this.state.attrs)
newAttrs = removeKeyFromObject("styling.backgroundColor", newAttrs)
this.minSize = {width: 50, height: 30}
this.state = {
...this.state,
size: { width: 120, height: 'fit' },
widgetName: "Radio button",
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
radios: {
label: "Radio Group",
tool: Tools.INPUT_RADIO_LIST,
@@ -180,8 +157,8 @@ export class RadioButton extends TkinterBase{
generateCode(variableName, parent){
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const config = convertObjectToKeyValueString(this.getConfigCode())
const code = [
`${variableName}_var = tk.IntVar()`,
@@ -193,7 +170,7 @@ export class RadioButton extends TkinterBase{
const radioBtnVariable = `${variableName}_${idx}`
code.push(`\n`)
code.push(`${radioBtnVariable} = tk.Radiobutton(master=${parent}, variable=${variableName}_var, text="${radio_text}")`)
code.push(`${radioBtnVariable}.config(bg="${bg}", fg="${fg}", value=${idx})`)
code.push(`${radioBtnVariable}.config(${config}, value=${idx})`)
code.push(`${radioBtnVariable}.${this.getLayoutCode()}`)
})

View File

@@ -4,7 +4,7 @@ import Widget from "../../../canvas/widgets/base"
import { removeKeyFromObject } from "../../../utils/common"
import { Tkinter_TO_WEB_CURSOR_MAPPING } from "../constants/cursor"
import { Tkinter_To_GFonts } from "../constants/fontFamily"
import { RELIEF } from "../constants/styling"
import { JUSTIFY, RELIEF } from "../constants/styling"
// TODO: add full width and full height in base widget
// TODO: the pack should configure width and height of widgets
@@ -204,7 +204,7 @@ export class TkinterBase extends Widget {
...layoutUpdates
}
console.log("new data: ", newData)
console.log("new data: ", newData, data)
this.setState(newData, () => {
let layoutAttrs = this.setParentLayout(parentLayout) || {}
@@ -286,12 +286,66 @@ export class TkinterWidgetBase extends TkinterBase{
label: "Relief",
tool: Tools.SELECT_DROPDOWN,
options: RELIEF.map((val) => ({value: val, label: val})),
value: "Arial",
value: "",
onChange: (value) => {
this.setWidgetInnerStyle("fontFamily", Tkinter_To_GFonts[value])
this.setAttrValue("font.fontFamily", value)
// this.setWidgetInnerStyle("fontFamily", Tkinter_To_GFonts[value])
this.setAttrValue("styling.relief", value)
}
}
},
// justify: {
// label: "Justify",
// tool: Tools.SELECT_DROPDOWN,
// options: JUSTIFY.map((val) => ({value: val, label: val})),
// value: "",
// onChange: (value) => {
// this.setWidgetInnerStyle("text-align", value)
// this.setAttrValue("styling.justify", value)
// }
// }
},
padding: {
label: "padding",
padX: {
label: "Pad X",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 1, max: 140},
value: null,
onChange: (value) => {
// this.setWidgetInnerStyle("paddingLeft", `${value}px`)
// this.setWidgetInnerStyle("paddingRight", `${value}px`)
const widgetStyle = {
...this.state.widgetInnerStyling,
paddingLeft: `${value}px`,
paddingRight: `${value}px`
}
this.setState({
widgetInnerStyling: widgetStyle
})
this.setAttrValue("padding.padX", value)
}
},
padY: {
label: "Pad Y",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 1, max: 140},
value: null,
onChange: (value) => {
const widgetStyle = {
...this.state.widgetInnerStyling,
paddingTop: `${value}px`,
paddingBottom: `${value}px`
}
this.setState({
widgetInnerStyling: widgetStyle
})
this.setAttrValue("padding.padX", value)
}
},
},
font: {
label: "font",
@@ -309,9 +363,8 @@ export class TkinterWidgetBase extends TkinterBase{
label: "font size",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 1, max: 140},
value: 14,
value: null,
onChange: (value) => {
console.log("font size: ", value)
this.setWidgetInnerStyle("fontSize", `${value}px`)
this.setAttrValue("font.fontSize", value)
}
@@ -321,7 +374,7 @@ export class TkinterWidgetBase extends TkinterBase{
label: "Cursor",
tool: Tools.SELECT_DROPDOWN,
toolProps: {placeholder: "select cursor"},
value: Tkinter_TO_WEB_CURSOR_MAPPING["arrow"],
value: "",
options: Object.keys(Tkinter_TO_WEB_CURSOR_MAPPING).map((val) => ({value: val, label: val})),
onChange: (value) => {
this.setWidgetInnerStyle("cursor", Tkinter_TO_WEB_CURSOR_MAPPING[value])
@@ -330,6 +383,39 @@ export class TkinterWidgetBase extends TkinterBase{
},
}
}
this.getConfigCode = this.getConfigCode.bind(this)
}
getConfigCode(){
const code = {
bg: `"${this.getAttrValue("styling.backgroundColor")}"`,
fg: `"${this.getAttrValue("styling.foregroundColor")}"`,
}
if (this.getAttrValue("styling.borderWidth"))
code["bd"] = this.getAttrValue("styling.borderWidth")
if (this.getAttrValue("styling.relief"))
code["relief"] = `"${this.getAttrValue("styling.relief")}"`
if (this.getAttrValue("font.fontFamily") || this.getAttrValue("font.fontSize")){
code["font"] = [`"${this.getAttrValue("font.fontFamily")}"`, this.getAttrValue("font.fontSize"), ]
}
if (this.getAttrValue("cursor"))
code["cursor"] = `"${this.getAttrValue("cursor")}"`
if (this.getAttrValue("padding.padX")){
code["padx"] = this.getAttrValue("padding.padX")
}
if (this.getAttrValue("padding.padY")){
code["pady"] = this.getAttrValue("padding.padY")
}
return code
}
}

View File

@@ -1,37 +1,21 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import {TkinterBase} from "./base"
import { convertObjectToKeyValueString } from "../../../utils/common"
import { TkinterWidgetBase } from "./base"
class Button extends TkinterBase{
class Button extends TkinterWidgetBase{
static widgetType = "button"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 80, height: 40 },
widgetName: "Button",
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
buttonLabel: {
label: "Button Label",
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
@@ -53,11 +37,12 @@ class Button extends TkinterBase{
generateCode(variableName, parent){
const labelText = this.getAttrValue("buttonLabel")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const config = convertObjectToKeyValueString(this.getConfigCode())
return [
`${variableName} = tk.Button(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}

View File

@@ -1,38 +1,21 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import {TkinterBase} from "./base"
import { convertObjectToKeyValueString } from "../../../utils/common"
import { TkinterWidgetBase } from "./base"
export class Input extends TkinterBase{
export class Input extends TkinterWidgetBase{
static widgetType = "entry"
constructor(props) {
super(props)
this.droppableTags = null // disables drops
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 120, height: 40 },
widgetName: "Entry",
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
placeHolder: {
label: "PlaceHolder",
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
@@ -52,12 +35,13 @@ export class Input extends TkinterBase{
generateCode(variableName, parent){
const placeHolderText = this.getAttrValue("labelWidget")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const placeHolderText = this.getAttrValue("placeHolder")
const config = convertObjectToKeyValueString(this.getConfigCode())
return [
`${variableName} = tk.Entry(master=${parent}, text="${placeHolderText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}
@@ -92,34 +76,18 @@ export class Input extends TkinterBase{
}
export class Text extends TkinterBase{
export class Text extends TkinterWidgetBase{
static widgetType = "Text"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 120, height: 80 },
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
placeHolder: {
label: "PlaceHolder",
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
@@ -138,6 +106,19 @@ export class Text extends TkinterBase{
this.setWidgetName("text")
}
generateCode(variableName, parent){
const placeHolderText = this.getAttrValue("placeHolder")
const config = convertObjectToKeyValueString(this.getConfigCode())
return [
`${variableName} = tk.Text(master=${parent}, text="${placeHolderText}")`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()

View File

@@ -1,8 +1,6 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import {TkinterBase, TkinterWidgetBase} from "./base"
import { Layouts } from "../../../canvas/constants/layouts"
import { convertObjectToKeyValueString } from "../../../utils/common"
import { TkinterWidgetBase } from "./base"
class Label extends TkinterWidgetBase{
@@ -12,29 +10,13 @@ class Label extends TkinterWidgetBase{
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
widgetName: "Label",
size: { width: 80, height: 40 },
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
labelWidget: {
label: "Text",
tool: Tools.INPUT,
@@ -67,11 +49,11 @@ class Label extends TkinterWidgetBase{
generateCode(variableName, parent){
const labelText = this.getAttrValue("labelWidget")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const config = convertObjectToKeyValueString(this.getConfigCode())
return [
`${variableName} = tk.Label(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}

View File

@@ -1,8 +1,9 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { TkinterBase } from "./base"
class MainWindow extends Widget{
class MainWindow extends TkinterBase{
static widgetType = "main_window"
@@ -29,6 +30,7 @@ class MainWindow extends Widget{
}
}
}
componentDidMount(){

View File

@@ -1,47 +1,27 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import { ArrowDownOutlined, DownOutlined } from "@ant-design/icons"
import {TkinterBase} from "./base"
import { DownOutlined } from "@ant-design/icons"
import { TkinterWidgetBase} from "./base"
import { convertObjectToKeyValueString } from "../../../utils/common"
class OptionMenu extends TkinterBase{
class OptionMenu extends TkinterWidgetBase{
static widgetType = "option_menu"
// FIXME: the radio buttons are not visible because of the default heigh provided
constructor(props) {
super(props)
this.droppableTags = null // disables drops
// const {layout, ...newAttrs} = this.state.attrs // Removes the layout attribute
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.minSize = {width: 50, height: 30}
this.state = {
...this.state,
isDropDownOpen: false,
widgetName: "Option menu",
size: { width: 120, height: 'fit' },
attrs: {
...newAttrs,
styling: {
label: "styling",
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
defaultValue: {
label: "Default Value",
tool: Tools.INPUT,
@@ -50,17 +30,19 @@ class OptionMenu extends TkinterBase{
this.setAttrValue("options", {inputs, selectedRadio})
}
},
options: {
widgetOptions: {
label: "Options",
tool: Tools.INPUT_RADIO_LIST,
value: {inputs: ["option 1"], selectedRadio: -1},
onChange: ({inputs, selectedRadio}) => {
this.setAttrValue("options", {inputs, selectedRadio})
this.setAttrValue("widgetOptions", {inputs, selectedRadio})
}
}
}
}
console.log("attrs1: ", this.state.attrs)
}
componentDidMount(){
@@ -68,11 +50,28 @@ class OptionMenu extends TkinterBase{
this.setWidgetInnerStyle("backgroundColor", "#fff")
}
generateCode(variableName, parent){
const config = convertObjectToKeyValueString(this.getConfigCode())
const defaultValue = this.getAttrValue("defaultValue")
const options = JSON.stringify(this.getAttrValue("widgetOptions").inputs)
return [
`${variableName}_options = ${options}`,
`${variableName} = tk.OptionMenu(master=${parent}, ${defaultValue}, *${variableName}_options)`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
const attrs = this.state.attrs
console.log("attrs: ", attrs)
return ({
id: this.__id,
widgetName: toolBarAttrs.widgetName,
@@ -90,7 +89,7 @@ class OptionMenu extends TkinterBase{
renderContent(){
const {inputs, selectedRadio} = this.getAttrValue("options")
const {inputs, selectedRadio} = this.getAttrValue("widgetOptions")
return (
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"

View File

@@ -1,35 +1,32 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import {TkinterBase} from "./base"
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
import {TkinterBase, TkinterWidgetBase} from "./base"
class Slider extends TkinterBase{
class Slider extends TkinterWidgetBase{
static widgetType = "scale"
constructor(props) {
super(props)
this.droppableTags = null // disables drops
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
widgetName: "Scale",
size: { width: 'fit', height: 'fit' },
attrs: {
...newAttrs,
...this.state.attrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
...this.state.attrs.styling,
// TODO: trough color
troughColor: {
label: "Trough Color",
tool: Tools.COLOR_PICKER,
value: "#fff",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
// this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.troughColor", value)
}
}
},
@@ -75,6 +72,25 @@ class Slider extends TkinterBase{
this.setAttrValue("styling.backgroundColor", "#fff")
}
generateCode(variableName, parent){
// TODO: add orientation
const config = this.getConfigCode()
config["_from"] = this.getAttrValue("scale.min")
config["to"] = this.getAttrValue("scale.max")
config["resolution"] = this.getAttrValue("scale.step")
const defaultValue = this.getAttrValue("defaultValue")
return [
`${variableName}_var = tk.DoubleVar(${defaultValue})`,
`${variableName} = tk.Scale(master=${parent}, variable=${variableName}_var)`,
`${variableName}.config(${config})`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()

View File

@@ -1,39 +1,23 @@
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
import { DownOutlined, UpOutlined } from "@ant-design/icons"
import {TkinterBase} from "./base"
import {TkinterBase, TkinterWidgetBase} from "./base"
class SpinBox extends TkinterBase{
class SpinBox extends TkinterWidgetBase{
static widgetType = "spin_box"
constructor(props) {
super(props)
this.droppableTags = null // disables drops
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 70, height: 'fit' },
widgetName: "Spin box",
attrs: {
...newAttrs,
styling: {
...newAttrs.styling,
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "#000",
onChange: (value) => {
this.setWidgetInnerStyle("color", value)
this.setAttrValue("styling.foregroundColor", value)
}
}
},
...this.state.attrs,
spinProps: {
label: "Properties",
display: "horizontal",
@@ -81,14 +65,27 @@ class SpinBox extends TkinterBase{
const min = this.getAttrValue("spinProps.min")
const max = this.getAttrValue("spinProps.max")
const step = this.getAttrValue("spinProps.step")
const defaultValue = this.getAttrValue("spinProps.defaultValue")
const defaultValue = this.getAttrValue("spinProps.default")
const config = {
from_: min,
to: max,
increment: step,
value: defaultValue,
...this.getConfigCode()
}
const code = []
let spinBox = `${variableName} = tk.Spinbox(master=${parent})`
if (defaultValue){
code.push(`${variableName}_var = tk.IntVar(${defaultValue})`)
spinBox = `${variableName} = tk.Spinbox(master=${parent}, textvariable=${variableName}_var)`
}
code.push(spinBox)
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
return [
`${variableName} = tk.Spinbox(master=${parent})`,
`${variableName}.config(bg="${bg}", fg="${fg}", from_=${min}, to=${max},
increment=${step})`,
...code,
`${variableName}.config(${convertObjectToKeyValueString(config)})`,
`${variableName}.${this.getLayoutCode()}`
]
}

View File

@@ -57,5 +57,16 @@ export function isNumeric(str) {
if (typeof str != "string") return false // we only process strings!
return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
!isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail
}
}
/**
* given an object with key value returns key1=value1, key2=value2 format
* @param {Object} obj
* @returns
*/
export function convertObjectToKeyValueString(obj){
return Object.entries(obj)
.map(([key, value]) => `${key}=${value}`)
.join(', ')
}