I'm using default example of NormalPeoplePicker from https://developer.microsoft.com/en-us/fluentui#/controls/web/peoplepicker#IPeoplePickerProps.
When the dropdown displays it cuts off longer items (example: 'Anny Lundqvist, Junior Manager of Soft..'). How do I make it wider, so that the full item's text displays?
import * as React from 'react';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { IPersonaProps } from 'office-ui-fabric-react/lib/Persona';
import { IBasePickerSuggestionsProps, NormalPeoplePicker, ValidationState } from 'office-ui-fabric-react/lib/Pickers';
import { people, mru } from '#uifabric/example-data';
const suggestionProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: 'Suggested People',
mostRecentlyUsedHeaderText: 'Suggested Contacts',
noResultsFoundText: 'No results found',
loadingText: 'Loading',
showRemoveButtons: true,
suggestionsAvailableAlertText: 'People Picker Suggestions available',
suggestionsContainerAriaLabel: 'Suggested contacts',
};
const checkboxStyles = {
root: {
marginTop: 10,
},
};
export const PeoplePickerNormalExample: React.FunctionComponent = () => {
const [delayResults, setDelayResults] = React.useState(false);
const [isPickerDisabled, setIsPickerDisabled] = React.useState(false);
const [mostRecentlyUsed, setMostRecentlyUsed] = React.useState<IPersonaProps[]>(mru);
const [peopleList, setPeopleList] = React.useState<IPersonaProps[]>(people);
const picker = React.useRef(null);
const onFilterChanged = (
filterText: string,
currentPersonas: IPersonaProps[],
limitResults?: number,
): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (filterText) {
let filteredPersonas: IPersonaProps[] = filterPersonasByText(filterText);
filteredPersonas = removeDuplicates(filteredPersonas, currentPersonas);
filteredPersonas = limitResults ? filteredPersonas.slice(0, limitResults) : filteredPersonas;
return filterPromise(filteredPersonas);
} else {
return [];
}
};
const filterPersonasByText = (filterText: string): IPersonaProps[] => {
return peopleList.filter(item => doesTextStartWith(item.text as string, filterText));
};
const filterPromise = (personasToReturn: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (delayResults) {
return convertResultsToPromise(personasToReturn);
} else {
return personasToReturn;
}
};
const returnMostRecentlyUsed = (currentPersonas: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
return filterPromise(removeDuplicates(mostRecentlyUsed, currentPersonas));
};
const onRemoveSuggestion = (item: IPersonaProps): void => {
const indexPeopleList: number = peopleList.indexOf(item);
const indexMostRecentlyUsed: number = mostRecentlyUsed.indexOf(item);
if (indexPeopleList >= 0) {
const newPeople: IPersonaProps[] = peopleList
.slice(0, indexPeopleList)
.concat(peopleList.slice(indexPeopleList + 1));
setPeopleList(newPeople);
}
if (indexMostRecentlyUsed >= 0) {
const newSuggestedPeople: IPersonaProps[] = mostRecentlyUsed
.slice(0, indexMostRecentlyUsed)
.concat(mostRecentlyUsed.slice(indexMostRecentlyUsed + 1));
setMostRecentlyUsed(newSuggestedPeople);
}
};
const onDisabledButtonClick = (): void => {
setIsPickerDisabled(!isPickerDisabled);
};
const onToggleDelayResultsChange = (): void => {
setDelayResults(!delayResults);
};
return (
<div>
<NormalPeoplePicker
// eslint-disable-next-line react/jsx-no-bind
onResolveSuggestions={onFilterChanged}
// eslint-disable-next-line react/jsx-no-bind
onEmptyInputFocus={returnMostRecentlyUsed}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={suggestionProps}
className={'ms-PeoplePicker'}
key={'normal'}
// eslint-disable-next-line react/jsx-no-bind
onRemoveSuggestion={onRemoveSuggestion}
onValidateInput={validateInput}
removeButtonAriaLabel={'Remove'}
inputProps={{
onBlur: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onBlur called'),
onFocus: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onFocus called'),
'aria-label': 'People Picker',
}}
componentRef={picker}
onInputChange={onInputChange}
resolveDelay={300}
disabled={isPickerDisabled}
/>
<Checkbox
label="Disable People Picker"
checked={isPickerDisabled}
// eslint-disable-next-line react/jsx-no-bind
onChange={onDisabledButtonClick}
styles={checkboxStyles}
/>
<Checkbox
label="Delay Suggestion Results"
defaultChecked={delayResults}
// eslint-disable-next-line react/jsx-no-bind
onChange={onToggleDelayResultsChange}
styles={checkboxStyles}
/>
</div>
);
};
function doesTextStartWith(text: string, filterText: string): boolean {
return text.toLowerCase().indexOf(filterText.toLowerCase()) === 0;
}
function removeDuplicates(personas: IPersonaProps[], possibleDupes: IPersonaProps[]) {
return personas.filter(persona => !listContainsPersona(persona, possibleDupes));
}
function listContainsPersona(persona: IPersonaProps, personas: IPersonaProps[]) {
if (!personas || !personas.length || personas.length === 0) {
return false;
}
return personas.filter(item => item.text === persona.text).length > 0;
}
function convertResultsToPromise(results: IPersonaProps[]): Promise<IPersonaProps[]> {
return new Promise<IPersonaProps[]>((resolve, reject) => setTimeout(() => resolve(results), 2000));
}
function getTextFromItem(persona: IPersonaProps): string {
return persona.text as string;
}
function validateInput(input: string): ValidationState {
if (input.indexOf('#') !== -1) {
return ValidationState.valid;
} else if (input.length > 1) {
return ValidationState.warning;
} else {
return ValidationState.invalid;
}
}
/**
* Takes in the picker input and modifies it in whichever way
* the caller wants, i.e. parsing entries copied from Outlook (sample
* input: "Aaron Reid <aaron>").
*
* #param input The text entered into the picker.
*/
function onInputChange(input: string): string {
const outlookRegEx = /<.*>/g;
const emailAddress = outlookRegEx.exec(input);
if (emailAddress && emailAddress[0]) {
return emailAddress[0].substring(1, emailAddress[0].length - 1);
}
return input;
}
Component which renders suggestion list have fixed width of 180px. Take a look at PeoplePickerItemSuggestion.styles.ts.
What you can do is to modify this class .ms-PeoplePicker-Persona:
.ms-PeoplePicker-Persona {
width: 260px; // Or what ever you want
}
UPDATE - Solution from comments
Change width trough styles property of PeoplePickerItemSuggestion Component
const onRenderSuggestionsItem = (personaProps, suggestionsProps) => (
<PeoplePickerItemSuggestion
personaProps={personaProps}
suggestionsProps={suggestionsProps}
styles={{ personaWrapper: { width: '100%' }}}
/>
);
<NormalPeoplePicker
onRenderSuggestionsItem={onRenderSuggestionsItem}
pickerCalloutProps={{ calloutWidth: 500 }}
...restProps
/>
Working Codepen example
For more information how to customize components read Component Styling.
Related
what exactly is happening is when i select the delivery service onChangeService i'm setting the next dropdown i.e. carrier's list.Also i'm setting the selected carrier as per the service.But all the time value is getting set correctly in {this.state.selectedCarrierName} but still i can show its label only.lists contain label an value pair
async onChangeService(value) {
//{ ...this.props.deliveryOptionsValue, selected: value.value === value }
this.setState({ newCarrierList: [], selectedCarrierName: '', selectedCararierName: '' });
const priceDeliveryService = this.props.orderDetailsDTO.priceDeliveryServiceDTOs;
console.log("carriers", JSON.stringify(this.props.carriers));
var newList = [];
var carrier = '';
await priceDeliveryService.forEach(m => {
console.log("compare", m.serviceId === value.id);
if (m.serviceId === value.id) {
console.log("carrier", JSON.stringify(this.props.carriers));
let l = this.props.orderDetailsDTO.carriers.filter(n => n.id === m.carrierId);
console.log("l", l);
if (l !== undefined) {
// / orderList: [...this.state.orderList, ...content]
// this.setState({ newCarrierList: [...this.state.newCarrierList, ...l[0]] });
newList.push(l[0]);
console.log("defa", newList);
if (m.defaultCarrierService) {
this.setState({ selectedCarrier: l[0], selectedCarrierName: l[0].name });
carrier = l[0].name;
} else {
this.setState({ selectedCarrier: newList[0], selectedCarrierName: newList[0].name });
carrier = newList[0].name;
}
}
}
});
const list = newList.map(item => ({
label: item.name,
value: item
}));
this.setState({ newCarrierListValue: list, newCarrierList: newList })
console.log("newCarrierListValue", list);
console.log("newCarrierList", newList);
// console.log("carrier service", p.carrierName, value.id);
const carrierServices = this.props.orderDetailsDTO.carrierServiceDTO;
console.log(carrierServices.length);
const pi = await this.setCarrierService(carrierServices, value, carrier);
// let pi;
// for (let i = 0; i < carrierServices.length; i++) {
// console.log("if", carrierServices[i].cdlServiceId == value.id, carrierServices[i].carrierName === carrier)
// if (carrierServices[i].cdlServiceId == value.id && carrierServices[i].carrierName === carrier) {
// console.log("mathced data", carrierServices[i]);
// pi = carrierServices[i];
// console.log(pi);
// break;
// }
// }
this.setState({ carrierService: pi.serviceName, carrierServices: carrierServices });
console.log(pi.serviceName);
}
<Dropdown required
label="Select delivery option"
data={this.props.deliveryOptionsValue}
// enableSearch
defaultValue={1}
value={this.state.selectedDeliveryOptionName}
onChange={(value) => {
console.log(" change value", value);
this.setState({ selectedDeliveryOptionName: value.name, selectedDeliveryOption: value, deliveryOptionError: false, });
this.onChangeService(value);
}} />
<Dropdown
required
label="Select a carrier"
data={this.state.newCarrierListValue}
// selectedValue={this.state.selectedCarrierName}
value={this.state.selectedCarrierName}
onChange={value => {
// console.log("value", value);
this.setState({ selectedCarrierName: value.name, selectedCarrier: value });
this.onChangeCarrier(value, this.state.selectedDeliveryOption);
}} />
I get the "object is not a function" error on the line where the "setup()" function appears.
I have no clue how to debug this... Am I doing something wrong? (I'm not an experimented developer, so I know I'm doing things wrong, but here, I have no clue what it is...)
It's asking me to add more details because my post is mainly code... So I'm writing this, but I habe idea what details I could c=possibly add. :-)
Thanks in advance for your help!!
<script>
import { ref } from 'vue'
import Ftable from '#/components/tables/Ftable.vue'
import Searchbar from '#/components/tables/Searchbar.vue'
import getMainCollection from '#/composables/getMainCollection'
export default {
name: 'Home',
components: { Searchbar, Ftable },
setup(){ //error arrives on this line
const { getData, getMore } = getMainCollection()
const documents = ref('')
const lastVisible = ref('')
const type = ref('')
const country = ref('')
function newSearch() {
const {doc, last} = getData(type, country)
documents.value = doc
lastVisible.Value = last
}
function askForMore() {
const { doc, last } = getMore(type, country, lastVisible)
documents.value = doc
lastVisible.value = last
}
return { documents, askForMore, newSearch, askForMore }
}
}
</script>
import { ref } from 'vue'
import { projectFirestore } from '#/firebase/config'
const getMainCollection = () => {
const collectionGroupRef = projectFirestore.collectionGroup('users')
const lastVisible = ref('')
const documents = ('')
function getData(type, country) {
const filter = null
if (type != null && country == null){
filter = collectionGroupRef.where('type', '==', `${type}`)
}
else if(type == null && country != null){
filter = collectionGroupRef.where('country', '==', `${country}`)
}
else{
filter = collectionGroupRef
}
const data = filter.orderBy('createdAt')
.limit(2)
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
documents.value.push({ ...doc.data(), id: doc.id})
})
lastVisible = querySnapshot.docs[querySnapshot.docs.length-1]
})
return( documents, lastVisible)
}
function getMore(type, country, lastVisible) {
const filter = null
if (type != null && country == null){
filter = collectionGroupRef.where('type', '==', `${type}`)
}
else if(type == null && country != null){
filter = collectionGroupRef.where('country', '==', `${country}`)
}
else{
filter = collectionGroupRef
}
filter.startAfter(lastVisible)
.limit(2)
.orderBy(createdAt)
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
documents.value.push({ ...doc.data(), id: doc.id })
})
lastVisible = querySnapshot.docs[querySnapshot.docs.length-1]
})
return( documents, lastVisible)
}
return ( getData, getMore )
}
export { getMainCollection }
Since you exported getMainCollection as an object:
export { getMainConnection }
You need to destructure it when importing:
import { getMainCollection } from '#/composables/getMainCollection'
I am having an issue with adding favourite functionality for e-commerce app. Getting error of undefined.enter image description here
case TOGGLE_FAVOURITE:
const exitsIndex = state.favouriteProducts.findIndex((meal) => meal.id === action.productId);
if(exitsIndex >= 0) {
return { ...state, favouriteProducts: state.favouriteProducts.filter((meal) => meal.id !== action.productId) }
} else {
const favMeal = state.availableProducts.find((meal) => meal.id === action.productId);
return { ...state, favouriteProducts: state.favouriteProducts.concat(favMeal) };
}
This is my action:
export const toggleFavourite = id => {
return { type: TOGGLE_FAVOURITE, productId: id };
};
And this is my call function:
const toggleFavouriteHandler = useCallback(() => {
dispatch(toggleFavourite(productId));
}, [dispatch, productId]);
you have to check for the initial state, when you are doing findIndex on favouriteProducts , favouriteProducts is null.
so just check if
state.favouriteProducts === null and that should work
To prevent TypeError you should check if state.favouriteProducts is undefined or null.
In this case, i would use:
case TOGGLE_FAVOURITE:
if (!state.favouriteProducts) return false; // you need this
const exitsIndex = state.favouriteProducts.findIndex((meal) => meal.id === action.productId);
if (exitsIndex >= 0) {
return { ...state, favouriteProducts: state.favouriteProducts.filter((meal) => meal.id !== action.productId) }
} else {
const favMeal = state.availableProducts.find((meal) => meal.id === action.productId);
return { ...state, favouriteProducts: state.favouriteProducts.concat(favMeal) };
}
I found different solution. Now its working
case TOGGLE_FAVOURITE:
const addedFavourite = action.product;
const id = addedFavourite.id;
const product_name = addedFavourite.product_name;
const product_image = addedFavourite.product_image;
const product_description = addedFavourite.product_description;
const product_price = addedFavourite.product_price;
const category_id = addedFavourite.category_id;
let updatedFavouriteItem = new Product(
id,
product_name,
product_image,
product_description,
product_price,
category_id,
)
if(state.items[addedFavourite.id] != null){
let updatedCartItems = { ...state.items };
delete updatedCartItems[action.product.id];
return {
...state,
items: updatedCartItems,
}
}
return{
...state,
items: {...state.items, [addedFavourite.id]: updatedFavouriteItem }
}
}
return state;
}
I want to hide the header on scroll in Ionic 4 Beta 5.
I tried all the directives solutions, but none of them work for me.
So, are there any methods that work?
Use below directive
import { IonContent, DomController } from '#ionic/angular';
import { Directive, ElementRef, Input, Renderer2, SimpleChanges } from '#angular/core';
#Directive({
selector: '[scrollHide]'
})
export class ScrollHideDirective {
#Input('scrollHide') config: ScrollHideConfig;
#Input('scrollContent') scrollContent: IonContent;
contentHeight: number;
scrollHeight: number;
lastScrollPosition: number;
lastValue: number = 0;
constructor(private element: ElementRef, private renderer: Renderer2, private domCtrl: DomController) {
}
ngOnChanges(changes: SimpleChanges) {
if(this.scrollContent && this.config) {
this.scrollContent.scrollEvents = true;
let scrollStartFunc = async (ev) => {
const el = await this.scrollContent.getScrollElement();
this.contentHeight = el.offsetHeight;
this.scrollHeight = el.scrollHeight;
if (this.config.maxValue === undefined) {
this.config.maxValue = this.element.nativeElement.offsetHeight;
}
this.lastScrollPosition = el.scrollTop;
};
if(this.scrollContent && this.scrollContent instanceof IonContent) {
this.scrollContent.ionScrollStart.subscribe(scrollStartFunc);
this.scrollContent.ionScroll.subscribe(async (ev) => this.adjustElementOnScroll(ev));
this.scrollContent.ionScrollEnd.subscribe(async (ev) => this.adjustElementOnScroll(ev));
} else if(this.scrollContent instanceof HTMLElement) {
(this.scrollContent as HTMLElement).addEventListener('ionScrollStart', scrollStartFunc);
(this.scrollContent as HTMLElement).addEventListener('ionScroll',async (ev) => this.adjustElementOnScroll(ev));
(this.scrollContent as HTMLElement).addEventListener('ionScrollEnd',async (ev) => this.adjustElementOnScroll(ev));
}
}
}
private adjustElementOnScroll(ev) {
if (ev) {
this.domCtrl.write(async () => {
const el = await this.scrollContent.getScrollElement();
let scrollTop: number = el.scrollTop > 0 ? el.scrollTop : 0;
let scrolldiff: number = scrollTop - this.lastScrollPosition;
this.lastScrollPosition = scrollTop;
let newValue = this.lastValue + scrolldiff;
newValue = Math.max(0, Math.min(newValue, this.config.maxValue));
this.renderer.setStyle(this.element.nativeElement, this.config.cssProperty, `-${newValue}px`);
this.lastValue = newValue;
});
}
}
}
export interface ScrollHideConfig {
cssProperty: string;
maxValue: number;
}
Steps to use:
In your HTML
<ion-header [scrollHide]="headerScrollConfig" [scrollContent]="pageContent">
.
.
.
<ion-content #pageContent>
In your controller: Add config variables
footerScrollConfig: ScrollHideConfig = { cssProperty: 'margin-bottom', maxValue: undefined };
headerScrollConfig: ScrollHideConfig = { cssProperty: 'margin-top', maxValue: 54 };
I am trying to enable date range using react-native-calendars. On my app, the calendar loads a few 'markedDates'; now I need to implement a start and end date functionality without affecting these initial dates. Unfortunately, I am struggling to achieve that. Any ideas on how can I do that?
Thank you in advance.
Pseudo-code
Load calendar with marked dates
Tap on start date
Tap on end date
Continue
Component
export default class Dates extends Component {
static navigationOptions = {
title: 'Choose dates',
}
constructor(props) {
super(props)
this.state = {
selected: undefined,
marked: undefined,
}
}
componentDidMount() {
this._markDate()
}
_markDate = () => {
nextDay = []
const marked = {
[nextDay]: { selected: true, marked: true },
}
Util._findShows(resp => {
resp.map(data => {
nextDay.push(data.date)
})
var obj = nextDay.reduce((c, v) => Object.assign(c, { [v]: { marked: true, dotColor: 'black' } }), {})
this.setState({ marked: obj })
})
}
_selectDate = obj => {
this.setState({ selected: obj.dateString })
}
render() {
return (
<View style={styles.container}>
<CalendarList
// Callback which gets executed when visible months change in scroll view. Default = undefined
onVisibleMonthsChange={months => {
console.log('now these months are visible', months)
}}
// Max amount of months allowed to scroll to the past. Default = 50
pastScrollRange={0}
// Max amount of months allowed to scroll to the future. Default = 50
futureScrollRange={12}
// Enable or disable scrolling of calendar list
scrollEnabled={true}
// Enable or disable vertical scroll indicator. Default = false
showScrollIndicator={true}
markedDates={
// [this.state.selected]: { selected: true, disableTouchEvent: true, selectedDotColor: 'orange' },
this.state.marked
}
onDayPress={day => {
this._selectDate(day)
}}
/>
<View style={styles.ctaArea}>
<TouchableOpacity style={styles.button} onPress={() => this.props.navigation.navigate('Dates')}>
<Text style={styles.btTitle}>Continue</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
I had the same struggle but so I decided to make my own version.
I'm sure it can be done better and have more functionalities but it works okay for me
const [dates, setDates] = useState({});
// get array of all the dates between start and end dates
var getDaysArray = function(start, end) {
for (var arr=[], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)){
arr.push(useFormatDate(new Date(dt)));
}
return arr;
};
// empty object
const emptyObj = (obj: Object) => {
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
delete dates[props[i]];
}
}
// check if first date is smaller or equals to second date
const compareDate = function(first: string, second: string) {
return (new Date(first) <= new Date(second) ? true : false);
}
// fill with color the date between first and second date selected
const fillRangeDate = function(first: string, second: string) {
emptyObj(dates);
let newDates = dates;
newDates[first]={selected: true, Colors[colorScheme].tint};
newDates[second]={selected: true, color: Colors[colorScheme].tint};
var range = getDaysArray(first, second);
for (var i = 1; i < range.length - 1; i++)
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
setDates({...newDates})
}
const selectDate = (day) => {
let selectedDate = day.dateString;
let newDates = dates;
// if 2 dates are selected
if (Object.keys(dates).length >= 2) {
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], selectedDate)) {
fillRangeDate(props[0], selectedDate);
} else {
emptyObj(dates);
}
} else {
// 1 date selected
if (Object.keys(dates).length == 0) {
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
} else if (Object.keys(dates).length == 1) { // 2 dates selected
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
// if 2nd date < 1st date, cancel range
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], props[1])) {
var range = getDaysArray(props[0], props[1]);
for (var i = 1; i < range.length - 1; i++) {
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
}
} else {
emptyObj(dates);
}
}
}
setDates({...newDates})
}
You'll also need to add this function that I implemented as a hook:
const useFormatDate = (date: Date) => {
function padTo2Digits(num) {
return num.toString().padStart(2, '0');
}
return [
date.getFullYear(),
padTo2Digits(date.getMonth() + 1),
padTo2Digits(date.getDate()),
].join('-');
};
Help me to improve this code and maybe create a merge request on the wix/react-native-calendar
Hope this helps