Is it possible to use Dropdown inside of Popover without Closing Popover? - antd

I am trying use dropdown inside popover but it close everytime popover. I tried using different visible property on state but still same.
<div>
<Dropdown
visible={visibleDropdown}
onVisibleChange={(visible)=>this.onDropdownVisibleChange(visible)}
overlay={
<Menu>
<Menu.Item key="0">
1st menu item
</Menu.Item>
<Menu.Item key="1">
2nd menu item
</Menu.Item>
<Menu.Divider />
<Menu.Item key="3">3d menu item</Menu.Item>
</Menu>
} trigger={['click']}>
<a className="ant-dropdown-link" href="#">
Click me <Icon type="down" />
</a>
</Dropdown>
</div>
This does not work am i missing something?

I played around a bit, and here is a working solution:
http://codepen.io/JesperWe/pen/QvMNjJ
Just using getPopupContainer doesn't solve it, you have to play with the visible state a bit. Looking at the most important parts from the Codepen:
onVisibleChange = ( visible ) => {
this.setState( { visible: true } )
};
closeDropdown = () => {
this.setState( { visible: false } )
};
...
const menu = (
<Menu onSelect={this.closeDropdown}>
<Menu.Item>
One
</Menu.Item>
<Menu.Item>
Two
</Menu.Item>
</Menu>
);
return (
<Dropdown overlay={menu} trigger={[ 'click' ]} getPopupContainer={this.getContainer}>
<Button onClick={this.onVisibleChange}>Dropdown</Button>
</Dropdown>
);
....
<Popover visible={this.state.visible}
onVisibleChange={this.onVisibleChange}
placement="right"
getPopupContainer={this.getContainer}
trigger="click" content={this.renderContent()}>
<Button icon="filter">click me</Button>
</Popover>
The trick is having onVisibleChange always set the state to true, and then closing the popover explicitly from the Dropdown overlay.

Related

Rendering new list item after adding it from a nested form. React hooks, redux, React Router V6

I am creating a list tracking app with React hooks, Redux, and Ruby on Rails. There is a List model, with a title as a string and completed as a boolean, and a ListItem model with descriptions as a string (the list item), completed boolean, and list_id as an integer.
I am using react route V6 for this and getting a little lost in re-rendering/ updating the page. Here is the breakdown of the application:
On the home screen, you can click to view all Lists and add a new list. when viewing all list each list title is displayed as a link to that list show page. The show page shows the list title, list items and a form to add another list item. Now where I am having trouble is being able to add a new list item, and it display on the page right after submission. Right now when I add a new item, and refresh the page it is not there. But if I click back to view all lists, then click that list again it shows up under the list items.
I tried using useNavigate to navigate to that list show page even though it is already on it but I am getting this error
Uncaught TypeError: Cannot destructure property 'list' of 'location.state' as it is null.
Here is all my components:
App.js
class App extends React.Component {
render(){
return (
<div className="App">
<Navbar/>
<br></br>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/lists" element={<Lists />} />
<Route path="/lists/new" element={<ListForm />} />
<Route path="/lists/:id" element={<ListContainer />} />
</Routes>
</div>
);
}
}
Lists.js
export default function Lists() {
const lists = useSelector(state => state.lists)
// replaces mapStateToProps
const dispatch = useDispatch()
// replaces mapDispatchToProps
useEffect(() => {
dispatch(fetchLists())
}, [])
return (
<div>
{Array.isArray(lists) && lists.map((list) => {
return (
<Link
key={list.id}
to={`/lists/${list.id}`}
state={{ list: list }}
>
<h2>{list.title}</h2>
</Link>
)
})}
</div>
)
}
ListContainer.js
export default function ListContainer() {
const location = useLocation();
const { list } = location.state;
console.log(list)
return (
<div>
<List list={list}/>
<ListItemForm list={list}/>
</div>
);
}
List.js
export default function List({list}) {
return (
<div>
<h4>{list.title}</h4>
{list.list_items.map((item) => {
return (
<div key={item.id}>
<li key={item.id}>{item.description}</li>
</div>
);
})}
<br></br>
</div>
);
}
and ListItemForm.js
export default function ListItemForm({list}) {
const [item, setItem] = useState("")
const dispatch = useDispatch()
const navigate = useNavigate()
function handleSubmit(e) {
e.preventDefault()
let newItem = {description: item, completed: false, list_id: list.id}
dispatch(createListItem(newItem, list.id))
setItem("")
navigate(`/lists/${list.id}`)
}
return (
<div>
<br></br>
<form onSubmit={handleSubmit}>
<label>Add to your list: </label>
<input value={item} onChange={(e) => setItem(e.target.value)} />
</form>
</div>
)
}
I have been stuck on this for quite some time now and not sure where to go from here or where I am going wrong. Any help is appreciated!!
Sometimes when you navigate to "/lists/:id" you send route state, sometimes you don't. It's undefined when you navigate to "/lists/:id" when adding new list items. This navigation to the route you are already on for editing a list is unnecessary.
Since you are using Redux I don't think there's any need to send a list item in route state at all. Use the id route parameter and your lists redux state to derive the specific list you want to view/edit.
Example
Given: <Route path="/lists/:id" element={<ListContainer />} />
Lists
function Lists() {
const dispatch = useDispatch();
const lists = useSelector((state) => state.lists);
useEffect(() => {
if (!lists.length) {
dispatch(fetchLists());
}
}, [dispatch, lists]);
return (
<div>
{lists.map((list) => (
<Link key={list.id} to={`/lists/${list.id}`}>
<h2>{list.title}</h2>
</Link>
))}
</div>
);
}
ListContainer
import { useParams } from 'react-router-dom';
function ListContainer() {
const { id } = useParams();
const lists = useSelector((state) => state.lists);
const list = lists.find((list) => list.id === id);
return (
<div>
<List list={list} />
<ListItemForm list={list} />
</div>
);
}
ListItemForm
function ListItemForm({ list }) {
const [item, setItem] = useState("");
const dispatch = useDispatch();
function handleSubmit(e) {
e.preventDefault();
dispatch(actions.createListItem(item, list.id));
setItem("");
}
return (
<div>
<br></br>
<form onSubmit={handleSubmit}>
<label>Add to your list: </label>
<input value={item} onChange={(e) => setItem(e.target.value)} />
</form>
</div>
);
}

How do I make a button behave like a checkbox with angular material when using a reactive form?

I have a button in a form group
<button mat-flat-button color="accent" matTooltip="Hide Section" formControlName="visible">
<mat-icon>visibility</mat-icon>
<!-- <mat-icon>visibility_off</mat-icon> -->
</button>
I know formControlName won't bind properly but is there an easy way to make the button toggle from the visibility icon to visibility_off icon just like a checkbox without coding a custom check box?
Thank you
we can solve this solution in many ways:
1.Use formGroup => code in .ts file:
form = new FormGroup({
visible: new FormControl(false),
});
in DOM:
<button mat-flat-button color="accent" formControlName="visible"
[matTooltip]="(form.get('visible').value ? 'Show' : 'Hide') + ' Section'"
(click)="form.get('visible').setValue(!form.get('visible').value)">
<mat-icon>
{{ form.get('visible').value ? 'visibility_off' : 'visibility'}}
</mat-icon>
</button>
2.Use formControl:
visible = new FormControl(false);
form = new FormGroup({
visible: this.visible,
});
and set this code in DOM:
<button mat-flat-button color="accent" [formControl]="visible"
[matTooltip]="(visible.value ? 'Show' : 'Hide') + ' Section'"
(click)="visible.setValue(!visible.value)">
<mat-icon>
{{ visible.value ? 'visibility_off' : 'visibility'}}
</mat-icon>
</button>
I recommend that call function for better coding:
(click)="onClickToggleButton()"
and implement this function in .ts file:
onClickToggleButton(): void {
this.visible.setValue(!this.visible.value);
}
You have mat-button-toggle is like a checkbox;
<mat-button-toggle value="CheckBox">CheckBox</mat-button-toggle>

jQuery Mobile Select Menu open on tap anywhere in Popup

I have a simple form I'm opening inside a popup:
<div data-role="popup" id="request-form" data-dismissible="false">
<div class="ui-header">
<h2>Request Holiday</h2>
</div>
<div>
<div data-role="fieldcontain" class="single-day">
<label><b>Date</b>: <span id="date"></span></label>
<select id="half-day">
<option value="Full">Full Day</option>
<option value="Morning">Morning Only</option>
<option value="Afternoon">Afternoon Only</option>
</select>
</div>
<button id="request-button" data-theme="B">Request</button>
<button id="cancel-button" data-rel="back">Cancel</button>
</div>
</div>
Which works fine, except when I click either of the buttons, or the label or the header in iOS, the select menu pops open - it appears to receive focus whenever a click/tap event fires in the popup. This only seems to happen when the form is in a popup.
I originally set out to use a dialog, but that causes my original page to lose it's scrolled position when I close the dialog, and I preferred the look of the popup.
Is this a bug? Is there a way I can stop the select automatically receiving focus? Any other workarounds?
Found a hacky solution, which involves extending the popup widget, overriding a method with the same code, but commenting out the code that causes the first focusable element to receive focus when the popup is clicked:
$.widget( "mobile.popup", $.mobile.popup, {
_openPrerequisitesComplete: function() {
var id = this.element.attr( "id" ),
firstFocus = this._ui.container.find( ":focusable" ).first();
this._ui.container.addClass( "ui-popup-active" );
this._isOpen = true;
this._resizeScreen();
// Check to see if currElement is not a child of the container. If it's not, blur
if ( !$.contains( this._ui.container[ 0 ], this.document[ 0 ].activeElement ) ) {
this._safelyBlur( this.document[ 0 ].activeElement );
}
// if ( firstFocus.length > 0 ) {
// this._ui.focusElement = firstFocus;
// }
this._ignoreResizeEvents();
if ( id ) {
this.document.find( "[aria-haspopup='true'][aria-owns='" + id + "']" ).attr( "aria-expanded", true );
}
this._trigger( "afteropen" );
},
});

dynamic jQuery Mobile SelectMenu is prematurely styling

Knockout is dynamically adding a select menu to a jQuery Mobile page. When it appears it has some select menu styling even though it hasn't been initialized as one. This causes a problem when I do initialize it because then it is wrapped in an extra ui-select. What is causing this and how can I fix it?
Here is an example. Check 'show options' to display the select. Then click one of the buttons to see the problem.
http://jsfiddle.net/5udqV/1/
Looking at your fiddle, the select is not dynamic, only the options within the select are. So one thing you could do is in the markup add data-role="none" to the select so that jQM does not touch it during page initialization. Then when you call .selectmenu() it will look right.
Your updated FIDDLE
UPDATE:
Use proper jQM structure and events:
DEMO
<div data-role="page" id="page1">
<div data-role="header">
<h1>My page</h1>
</div>
<div role="main" class="ui-content">show options
<input type="checkbox" data-bind="checked: showOptions" />
<br />
<div data-bind="if: showOptions">
<select data-bind="options: options, value: selectedOption"></select>
</div>
<button id="a">create select</button>
<button id="b">refresh select</button>
<button id="c">create page</button>
<div data-bind="text: ko.toJSON($root)"></div>
</div>
</div>
var vm = {
options: ["A", "B", "C"],
showOptions: ko.observable(),
selectedOption: ko.observable("B")
};
ko.applyBindings(vm);
$(document).on("pagecreate", "#page1", function () {
$('button').on("click", function () {
var id = $(this).prop("id");
if (id == "a") {
$("select").selectmenu();
} else if (id == "b") {
$("select").selectmenu("referesh");
} else if (id == "c") {
$(".ui-page").trigger("create");
}
});
});

jquery ui tabs functions for each tab

How do you bind a function for each index of the jquery UI tabs?
For example, I am creating a 3 part slide sign up, step 1 is a form and has validation, I want to place the code for that inside the load of step1, while also adding classes to the tabs to disable #2 and #3 when on 1, disable #1 and # 3 when on #2
There is no need to bind a function to the tabs, it's built into the plugin:
From the plugin page:
$('#tabs').tabs({
select: function(event, ui) { ... }
});
Inside the function, you can determine the current tab you are in and do what you need to do from there:
Get the current tab index
currentTabIndex = $('#tabs').tabs('option', 'selected')
Get the current tab content ID (from href) - there might be an easier way, but I haven't found it yet.
currentTabContent = $( $('.ui-tabs-selected').find('a').attr('href') );
but from seeing the other questions you posted about this tab/form system you are trying to use, I threw together a demo here.
HTML
<div id="tabs">
<ul class="nav"> <!-- this part is used to create the tabs for each div using jquery -->
<li class="ui-tabs-selected"><span>One</span></li>
<li><span>Two</span></li>
<li><span>Three</span></li>
</ul>
<div id="part-1">
<form name="myForm" method="post" action="" id="myForm">
<div class="error"></div>
Part 1
<br /><input type="checkbox" /> #1 Check me!
<br />
<br /><input id="submitForm" type="button" disabled="disabled" value="next >>" />
</form>
</div>
<div id="part-2">
<div class="error"></div>
Part 2
<br />Search <input type="text" />
<br />
<br /><input id="donePart2" type="button" value="next >>" />
</div>
<div id="part-3">
<div class="error"></div>
Part 3:
<br />Some other info here
</div>
</div>
Script
$(document).ready(function(){
// enable Next button when form validates
$('#myForm').change(function(){
if (validate()) {
$('#submitForm').attr('disabled','')
} else {
$('#submitForm').attr('disabled','disabled')
}
})
// enable form next button
$('#submitForm').click(function(){
// enable the disabled tab before you can switch to it, switch, then disable the others.
if (validate()) nxtTab(1,2);
})
// enable part2 next button
$('#donePart2').click(function(){
var okForNext = true; // do whatever checks, return true
if (okForNext) nxtTab(2,1);
})
// Enable tabs
$('#tabs').tabs({ disabled: [1,2], 'selected' : 0 });
})
function validate(){
if ($('#myForm').find(':checkbox').is(':checked')) return true;
return false;
}
function nxtTab(n,o){
// n = next tab, o = other disabled tab (this only works for 3 total tabs)
$('#tabs').data('disabled.tabs',[]).tabs( 'select',n ).data('disabled.tabs',[0,o]);
}

Resources