I am creating a custom input via the component method with formkit.
I have the computed which looks like this
<template>
<label
class="InputCheckboxFeature"
:for="props.context.id"
>
<input
:id="props.context.id"
type="checkbox"
class="sr-only"
:checked="checked"
#input="handleInput"
:value="props.context._value"
/>
<span class="InputCheckboxFeature__indicator">
<span class="space-x-4 inline-flex items-center">
<span
v-if="props.context.attrs.icon"
class="InputCheckboxFeature__icon"
v-html="props.context.attrs.icon"
>
</span>
<span class="inline-block">
{{ props.context.label }}
</span>
</span>
<ButtonInfo
v-if="props.context.tooltip"
class="flex-shrink-0"
:text="props.context.tooltip"
aria-label="More Info"
/>
</span>
</label>
</template>
I then have a plugin that strips out all the HTML elements so the custom input will only output the component markup
export const createCleanInputPlugin = (): FormKitPlugin => {
return (node: FormKitNode) => {
node.on('created', () => {
const { props } = node;
if (
!(
props.type === InputType.FEATURED_CHECKBOX &&
props.definition &&
typeof props.definition.schema === 'function'
)
) {
return;
}
const definition = { ...props.definition };
const schema = definition.schema as FormKitExtendableSchemaRoot;
// We replace the schema function with our own higher-order-function
definition.schema = function (extensions = {}) {
const ext = {
...extensions,
...{
inner: { $el: null },
outer: { $el: null },
label: { $el: null },
wrapper: { $el: null },
},
};
// Finally we call the original schema, with our extensions applied
return schema(ext);
};
// Now we replace the input definition
props.definition = definition as FormKitTypeDefinition;
});
};
};
The problem I am facing is that when the element is rendered it is outputting the label twice.
Is it possible to hide the label, and only have it render in the component?
I want to display default image instead of white space
Swatch Image
for example https://cdn11.bigcommerce.com/s-hmhnh4h9/images/stencil/70w/attribute_value_images/252766.preview.jpg this return 404
while this returns 200 https://cdn11.bigcommerce.com/s-hmhnh4h9/images/stencil/70w/attribute_value_images/252738.preview.jpg
I even tried fetch method but still it has same result
I have built a function to detect if image loads or fail
`
async function getImageOrFallback(url: string, fallback: string) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = url
img.onload = () => resolve(url)
img.onerror = () => resolve(fallback);
}).catch(() => {
return fallback
})
}
`
Under swatch if image url is invalid then a default image needed to be displayed
`
<div className="option-section">
<div id="alloptions" className="flex_div flex justify-between flex-wrap flex-row py-4">
<input id="selected_variant" type="hidden" value={currentVariant.id} />
{variants.data.sort((a: any, b: any) => a.option_values[0].label.replace(/[^A-Za-z]/g, '').localeCompare(b.option_values[0].label.replace(/[^A-Za-z]/g, ''))).sort((a:any,b:any)=>{
if(a.option_values[0].label.replace(/[^A-Za-z]/g, '')[0] == b.option_values[0].label.replace(/[^A-Za-z]/g, '')[0]){
return a.option_values[0].label.replace(/[^0-9]/g, '').localeCompare(b.option_values[0].label.replace(/[^0-9]/g, ''));
}
}).sort((a:any,b:any) => {
if(variantCount < 8){
if (a.inventory_level === 0) {
return 1;
}
if (b.inventory_level === 0) {
return -1;
}
if (a.inventory_level === b.inventory_level) {
return 0;
}
return a < b ? -1 : 1;}else{
return false;
}
}).map((v: any, i: number) => {
let pUrl = product.path.replace('.html', '')
const opt = v.option_values[0]
const active = selectedOptions[opt.option_display_name.toLowerCase()]
// const swatch = v.image_url;
const swatch =
publicRuntimeConfig.COLOR_SWATCH_URL +
'/stencil/70w/attribute_value_images/' +
opt.id +
'.preview.jpg'
var imageUrl = getImageOrFallback(`${swatch}`, '/product-img-placeholder.svg').then(validUrl => {
validUrl
})
console.log("!!!!!!!!!!!!!!!! ", imageUrl)
const isDisabled = v.inventory_level == 0 ? ' color_disabled' : ' color_enabled'
const itemQty = items ? items[v.id] : 0
return (
<Swatch
key={`${opt.id}-${i}`}
active={v.sku}
variant={opt.option_display_name}
color={v.hexColors ? v.hexColors[0] : ''}
image={swatch ? swatch : ''}
label={opt.label}
id={itemQty}
className={customclass + isDisabled}
onClick={() => {
loadOptions(v);
setCurrentVariant(event, v, null);
}}
/>
)
})}
</div>
</div>
enter image description here
Under the Swatch component default image needed to display
i have a problem with framer-motion and the "exit"-animation.
After some search on the Internet i found out that the children of the <AnimatePresence> need a key prop and should be the direct child.
My simplified structure:
// manager of the sites
const SiteManager = () => {
return (
<AnimatePresence mode="wait" ...>
{
{
0: <Page1 />
1: <Page2 />
...
}[page]
}
</AnimatePresence>
)
}
// this component should be animated with the slide effect
const Fade = ({ children }) => {
return (
<motion.div key={page} ...>
{ children }
</motino.div>
)
}
// a page has content and a footer -> footer shouldnt be animated, thats the reason why i had to seperate it to the <Fade /> Component
const Page1 = () => {
const [value, setValue] = useState("")
return (
<>
<Fade>
<input value={value} onChange={e => setValue(e.target.value)} />
</Fade>
<Footer value={value} ... />
</>
)
}
Maybe the codesandbox helps a bit:
Codesandbox
I gave the <motion.div> a key, but it doenst change anything.
I have a structure of data i want to fetch and display in a Flat List here is my data.
-Lt10FlMt0xtru36Ztmb
name : "Hello"
-Lt0pdC5Ikwd-ZWNBiYJ
name : "Coke"
-Lt0paPi_-zkCelfoisM
name : "Pespsi"
Here is my code:
readUserData = () => {
firebase.database().ref('Brands/').once('value').then((snapshot) => {
console.log(snapshot.val())
const userdata = snapshot.val();
// Alert.alert("Helo" , userdata);
this.setState({
getListbrands : userdata
})
Alert.alert("Data" , JSON.stringify(this.state.getListbrands))
});
}
and here i am setting data in Flat list but it is not showing any thing please guide.
{
this.state.getListbrands &&
<FlatList
data={this.state.getListbrands}
keyExtractor={(a, b) => b.toString()}
renderItem={({ item }) => (
<Text style={{color:'#000'}}>{item.name}</Text>
)}
/>
}
According to the documentation, FlatList component expects an array of data. The value of your snapshot is an object, and not an array.
If you want to use an array of the values of each child node, you can get that with:
firebase.database().ref('Brands/').once('value').then((snapshot) => {
userdata = [];
snapshot.forEach((child) {
userdata.push(snapshot.val());
})
...
You can then set this array to the FlatList.
{
this.state.getListbrands &&
<FlatList
data={this.state.getListbrands}
keyExtractor={(a, b) => b.toString()}
renderItem={(item) => (
<Text style={{color:'#000'}}>{item.name}</Text>
)}
/>
}
change like this.i think item in here is not a object.please try. it will work. thank you.
I use Rails and React-Table to display tables. It works fine so far. But How can one add an edit/delete column to the React-Table?
Is it even possible?
return (
<ReactTable
data={this.props.working_hours}
columns={columns}
defaultPageSize={50}
className="-striped -highlight"
/>
)
All you need to do is turn columns into a component state. You can see a working example https://codesandbox.io/s/0pp97jnrvv
[Updated 3/5/2018] Misunderstood the question, here's the updated answer:
const columns = [
...
{
Header: '',
Cell: row => (
<div>
<button onClick={() => handleEdit(row.original)}>Edit</button>
<button onClick={() => handleDelete(row.original)}>Delete</button>
</div>
)
}
]
where handleEdit and handleDelete will be the callbacks how you want to handle the actions when the buttons are clicked.
You can actually add buttons using the accessor prop of the columns in react-table.
here is code example:
{
Header: 'Action',
accessor: (originalRow, rowIndex) => (
<div>
<button onClick={() => handleEdit(originalRow)}>Edit</button>
<button onClick={() => handleDelete(originalRow)}>Delete</button>
</div>
),
id: 'action',
},
ReactTable v7
Add a column into a table can be achieved by inserting a column object into columns definitions that pass into useTable hook. Basically, the number of column objects that resides in the columns definitions array represents the number of columns that will be rendered by react-table.
Usually, a minimal column object consists of Header and accessor which at the Header we pass the name of the column and at the accessor, we pass the key that will be used by the react-table to look up the value from the data passed into useTable hook.
{
Header: "Column Name",
accessor: "data key", // can be a nested key
}
Here, to render other than a string inside a cell, which is, in this case, is custom button JSX, we can use either accessor or Cell option and pass to it a Function that returns a valid JSX.
accessor
The document says that accessor accept either string or Function Here we use Function to render a JSX button.
accessor: String | Function(originalRow, rowIndex) => any
One of the benefits of using accessor options is the rowIndex is directly available. The rowIndex represents the index number of rows inside the data array that is currently managed by react-table on the client-side and the originalRow is the raw object of row.
Here the rowIndex can be used as a reference to select and modify row objects in the columns data array.
Cell
Cell option accept Function that return either JSX or React.Component. Cell option is usually used for formatting a cell value, but here we use to render our button.
Cell: Function | React.Component => JSX
The Function receives tableInstance that is quite similar to the result of useTable hook with additional cell, row, and column object.
Cell: (tableInstance) => JSX
Here we can get the row index information too by destructuring the row object:
Cell: (tableInstance) => {
const { row: index } = tableInstance;
return (
...
)
}
So, it depends on your requirement in determining whether accessor or Cell will be the chosen one for rendering your edit/add button. But if you need to get more data/information from tableIntance, then Cell is the correct option to go.
Note: If you choose accessor, please make sure that the id is included in the column properties due to the id option being required as the document said so.
Required if accessor is a function.
This is the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.
Now, we already have the column. The next is the button. Commonly the button is either a normal button that will either call a handler for updating a state or triggering a dialog popup or a link button that will redirect the app to the detail page.
So, the code will be:
// accessor
{
Header: 'Action',
id: 'action',
accessor: (originalRow, rowIndex) => {
return (
// you can pass any information you need as argument
<button onClick={() => onClickHandler(args)}>
X
</button>
)
}
}
// or Cell
{
Header: 'Action',
accessor: "action",
Cell: (tableInstance) => {
const { row: index } = tableInstance;
return (
// you can pass any information you need as argument
<button onClick={() => onClickHandler(args)}>
X
</button>
)
}
}
Example:
const { useCallback, useEffect, useMemo, useState } = React;
const { useTable } = ReactTable;
// table data
const data = [
{
name: "John",
workingHours: 40
},
{
name: "Doe",
workingHours: 40
}
];
const AddEmployee = ({ onSubmit }) => {
const [name, setName] = useState("");
const [workingHours, setWorkingHours] = useState("");
const handleSubmit = (e) => {
onSubmit(e);
setName("");
setWorkingHours("");
}
return (
<fieldset style={{ width: "200px" }}>
<legend>Add Employee:</legend>
<form onSubmit={(e) => handleSubmit(e)}>
<input
type="text"
name="name"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
type="text"
name="workingHours"
placeholder="Working Hours"
value={workingHours}
onChange={(e) => setWorkingHours(e.target.value)}
/>
<br />
<button type="submit">Add</button>
</form>
</fieldset>
)
}
const EditEmployee = ({ row, onSave }) => {
const { originalRow, rowIndex } = row;
const [name, setName] = useState(originalRow.name);
const [workingHours, setWorkingHours] = useState(originalRow.workingHours);
return (
<fieldset style={{ width: "200px" }}>
<legend>Edit Employee:</legend>
<input
type="text"
name="name"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
type="text"
name="workingHours"
placeholder="Working Hours"
value={workingHours}
onChange={(e) => setWorkingHours(e.target.value)}
/>
<br />
<button onClick={() => onSave({ name, workingHours }, rowIndex)}>Save</button>
</fieldset>
)
}
function App() {
const [tableData, setTableData] = useState(data);
const [editingRow, setEditingRow] = useState();
const handleDelete = useCallback((index) => {
setTableData(tableData.filter((v, i) => i !== index));
},[tableData]);
const tableColumns = useMemo(() => [
{
Header: 'Name',
accessor: 'name',
},
{
Header: 'Working Hours',
accessor: 'workingHours'
},
{
Header: 'Action',
id: 'action',
accessor: (originalRow, rowIndex) => {
return (
<div>
<button onClick={() => setEditingRow({ originalRow, rowIndex })}>
Edit
</button>
<button onClick={() => handleDelete(rowIndex)}>
Delete
</button>
</div>
)
}
}
], [handleDelete]);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({
columns: tableColumns,
data: tableData,
});
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const newData = {};
formData.forEach((value, key) => newData[key] = value);
setTableData((prevData) => {
return [...prevData, newData];
});
};
const handleEdit = useCallback((row, rowIndex) => {
const editedData = tableData.map((rowData, index) => {
if (index === rowIndex) {
return row;
}
return rowData;
});
setTableData(editedData);
setEditingRow();
},[tableData])
return (
<div>
<h3>React-table v.7</h3>
<br />
{ editingRow ? <EditEmployee row={editingRow} onSave={handleEdit} /> : <AddEmployee onSubmit={handleSubmit} /> }
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-table#7.8.0/dist/react-table.development.js"></script>
<div class='react'></div>