I'm using the Ant Design Calendar Component in my project and I have it set up as follows:
Currently the Day of Week format is dd. Eg. Su, Mo, Tu, etc.
Is it possible to change the format via props to ddd. Eg. Sun, Mon, Tue, etc.?
There is no support for changing that directly on ant design component,
but Ant Design under the hood is using moment which is using locale.weekdaysMin, so you can import moment and change them:
import moment from 'moment'
moment.updateLocale('en', {
weekdaysMin : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
});
Changing the configuration of moment will cause global changes. If you are absolutely sure you want all the stuff relying on moment's minimized English weekday names to be set to "Sun Mon Tue....", then that's fine, and you should go with Barry Michael Doyle's answer.
If you only want to apply this change to specific calendars, however, I'd recommend using the CSS ::after pseudoclass.
First, apply some class name to the calendar that you can use for CSS targeting.
import React from "react";
import { Calendar } from "antd";
export const App = () => <Calendar className="my-calendar" />;
Then you can add the extra letters using CSS:
/* adds 'n' after "Su"*/
.my-calendar table thead tr > th:nth-child(1)::after {
content: "n";
}
/* adds 'n' after "Mo"*/
.my-calendar table thead tr > th:nth-child(2)::after {
content: "n";
}
/* ... etc., etc., ....*/
.my-calendar table thead tr > th:nth-child(3)::after {
content: "e";
}
.my-calendar table thead tr > th:nth-child(4)::after {
content: "d";
}
.my-calendar table thead tr > th:nth-child(5)::after {
content: "u";
}
.my-calendar table thead tr > th:nth-child(6)::after {
content: "i";
}
.my-calendar table thead tr > th:nth-child(7)::after {
content: "t";
}
Note: If you need to support different languages, this solution would require you to set the class name dynamically based on language settings and have different sets of the above CSS for each language. For one or two languages that won't be too bad, but beyond that you might want to take a different approach.
You must first set the format using a variable:
const dateFormat = 'DDD/MM/YYYY';
and then in the DatePicker you point out that you are going to use a custom format:
<DatePicker format = {dateFormat} />
The other way is to render each cell of the Datepicker placing the number in custom form:
<DatePicker
format = {dateFormat}
dateRender = {(current) => {
const num = (current.toString().length < 2) ? "0" + current : current)
return (
{num}
)
}}
/>
Related
I am using fullcalendar with react. I am trying to customize the dayGrid view. According to the Content Injection docs for react I can use custom content for the rendering of both the date and the header cells. The dayCellContent "hook" states that:
Generated content is inserted inside the inner-most wrapper of the day cell. It does not replace the cell.
I've provided an implementation for the dayCellContent and noticed that my content gets injected into the following structure:
<td class="fc-daygrid-day fc-day fc-day-wed fc-day-past rot_time-off_day-cell" data-date="2021-04-07">
<div class="fc-daygrid-day-frame fc-scrollgrid-sync-inner">
<div class="fc-daygrid-day-top">
<a class="fc-daygrid-day-number">
...custom content goes here
</a>
</div>
<div class="fc-daygrid-day-events"></div>
<div class="fc-daygrid-day-bg"></div>
</div>
</td>
Now, the problem is that this structure lets you insert content ONLY in the upper right corner of the date cell due to the positioning of the element. Furthermore, it is in an anchor element.
Example:
function renderDayCell(dayCellContent: DayCellContentArg) {
return (
<div>
{dayCellContent.dayNumberText}
</div>
);
}
Is there a clean way to customize the whole content of the cell somehow? I've seen a couple of sites using fullcalendar that have their content inserted directly into the td. Not sure if this is version dependent or they're using the alternative JS approach based on domNodes or html. I am using version 5.6.0 of fullcalendar.
I had the same requirement although not using React. I solved it using a manual manipulation of the DOM elements as suggested above. I have used jQuery for the select and manipulation. It is posted here in case anyone would like to see an example of how this can be achieved using DOM manipulation.
I implemented dayCellContent to make the day-cell DOM element easily identifiable by wrapping it in a span, with a unique id attribute based on the day of year number:
dayCellContent: function(info, create) {
const element = create('span', { id: "fc-day-span-"+info.date.getDayOfYear() }, info.dayNumberText);
return element;
},
This dayCellContent implementation makes no visible difference to the calendar but makes it easier to identify the elements to be modified in the DOM.
I then implemented dayCellDidMount to do the DOM manipulation by finding the appropriate cells and selecting their parent’s parent:
dayCellDidMount: function(info) {
let element = "<div style='position: absolute; left: 4px; top: 4px;'><a href='https://www.w3schools.com/'>TEST-"+info.dayNumberText+"</a></div>";
$('#fc-day-span-'+info.date.getDayOfYear()).parent().parent().prepend(element);
},
In this case I have just put a link to w3c in the top left of the cell with test text which also includes the day number. It results in cells that look like this:
Clearly the CSS could be improved and should be moved out to the CSS definitions but it illustrates the point.
Warning: This approach makes assumptions about the DOM structure that FullCalendar generates. The generated HTML may change in future versions of the product which could invalidate it. If you go this way then be careful when doing a FullCalendar update.
Note that the getDayOfYear function is from the ext-all.js library. Any way of uniquely identifying the day will work.
ngAfterViewInit(){
// Your CSS as text
var styles =.fc td, .fc th { vertical-align: top; padding: 0; height: 100px; } a{ color:#3d1cba; }
let styleSheet = document.createElement("style");
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
let arrTD = document.querySelectorAll('td.fc-timeline-slot');
let arrTR= document.querySelectorAll('td.fc-timeline-lane.fc-resource');
let arrInject= document.querySelectorAll('td.fc-timeline-lane.fc-resource>div.fc-timeline-lane-frame');
console.log(arrTR);
let k=-1;
arrTR.forEach(eachTR => {
let i=1;
let str = '';
k++;
let data_resource_id= eachTR.getAttribute('data-resource-id');
console.log(data_resource_id);
arrTD.forEach(eachTD => {
let k=100*(i-1);
i=i+1;
let data_date= eachTD.getAttribute('data-date');
console.log(data_date);
let data_resource_id= eachTR.getAttribute('data-resource-id');
console.log(data_resource_id);
str = str + '<span data-date="'+data_date+'" data-resource-id="'+data_resource_id+'" class="plus_icon" style="position:relative;top: 0px; left: '+k+'px !important;width:500px;height:500px;z-index:3;-moz-border-radius:100px;border:1px solid #ddd;-moz-box-shadow: 0px 0px 8px #fff;">+</span>';
});
arrInject[k].innerHTML=str;
});
let elementList = this.elRef.nativeElement.querySelectorAll('span.plus_icon');
for(let i=0;i<elementList.length;i++){
elementList[i].addEventListener('click', this.plusClick.bind(this));
}
}
Is there any way to use the date picker to simply display blocked out date ranges as opposed actually allowing the user to pick date ranges.
A good example of the functionality I require is the availability display that Airbnb use for each property:
example
Solved this now. In the end, I did the following:
import { DayPickerSingleDateController } from 'react-dates';
....
const [focusedInput, setFocusedInput] = useState('startDate');
<DayPickerSingleDateController
...
onFocusChange={focusedInput => {
return true;
}}
...
>
I have the following React component which I would like to reuse in several applications:
import React from "react";
import { useFactory } from "react-js-utl/hooks";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
/**
* Shows current account balance with variation since previous balance.
*/
const Balance = compose(React.memo)(function Balance({
current,
previous,
label,
sinceLabel,
increaseColor = "light-green",
increaseIcon = "arrow-up",
decreaseColor = "red",
decreaseIcon = "arrow-down",
sameColor = "blue",
sameIcon = "equals",
formatNum = void 0,
formatPerc = void 0
} = {}) {
// Compute the change.
let change = ((current - previous) / previous) * 100;
// Implementation detail. Switch objects depeding on runtime value of change.
const outcomeFactory = useFactory(
() => [
// Increase:
[
change > 0, // Condition, if true, the object following it will be returned by `useFactory()`.
{
outcome: "increase",
color: increaseColor,
icon: increaseIcon
}
],
// Decrease:
[
change < 0,
{
outcome: "decrease",
color: decreaseColor,
icon: decreaseIcon
}
],
// Same (default, returned if the previous conditions evaluate to false):
{
outcome: "same",
color: sameColor,
icon: sameIcon
}
],
// deps array
[
change,
increaseColor,
increaseIcon,
decreaseColor,
decreaseIcon,
sameColor,
sameIcon
]
);
change = Math.abs(change);
return (
<div className="balance">
<div className="balance-label">{label}</div>
<div className="balance-current">
{formatNum ? formatNum(current) : current}
</div>
<span
className={`balance-outcome balance-outcome-${outcomeFactory.outcome} balance-outcome-${outcomeFactory.color}`}
>
{typeof outcomeFactory.icon === "string" ? (
// Defaults to FontAwesome's icon.
<FontAwesomeIcon icon={outcomeFactory.icon} />
) : (
// Custom component rendered by client code.
outcomeFactory.icon
)}
{outcomeFactory.outcome === "same"
? // Same (no change since previous):
""
: // Increase or decrease since previous:
// Edge case: previous balance was zero
Number(previous) === 0
? ""
: // Percentage increase/decrease:
formatPerc
? formatPerc(change)
: change}
</span>
<span className="balance-since-label">{sinceLabel}</span>
</div>
);
});
Balance.displayName = "Balance";
export default Balance;
Which I use in an app like this:
...
function myFormattingFunctionForNumbers(t, num) {
// I use accounting.js to format numbers:
return accounting.format(num, {
decimal: t("formatting:numbers.decimal_separator"), // For EN lang this will be "."
thousand: t("formatting:numbers.thousand_separator") // For EN this this will be ","
})
}
function myFormattingFunctionForPercentages(t, perc) {
return myFormattingFunctionForNumbers(t, perc) + "%";
}
...
// Inside app component:
// Using react-i18next:
const { t, i18n } = useTranslation();
// If the language changes, so does formatting:
formatNum = useCallback(current => myFormattingFunctionForNumbers(t, current), [t, i18n.language])
formatPerc = useCallback(changePerc => myFormattingFunctionForPercentages(t, changePerc), [t, i18n.language])
...
<Balance
current={11234.56} // These hardcoded values could be props, of course.
previous={9321.45}
label={t("Your account balance")} // i18n
sinceLabel={t("Since previous month")} // i18n
formatNum={formatNum} // Function to format current value.
formatPerc={formatPerc} // Function to format change/variation percentage.
/>
...
Which outputs something looking roughly like this:
Your account balance
11,234.56
↑ 20,52% Since previous month
Right now I am facing the following "issues":
The reusable component is pure (uses React.memo), so its formatting functions formatNum and formatPerc need to change when the app's language changes even if the other props like current and previous don't change because a different language potentially involves different formatting and therefore the component should rerender;
Because of point 1, the client is responsible of wiring all the formatting functions inside the consuming component using useCallback which creates lot of biolerplate...;
The two useCallbacks do not warn me that I need to pass i18n.language to the deps array, simply because the current language is not referenced directly by the formatting functions myFormattingFunctionForNumbers and myFormattingFunctionForPercentages, which only use the t function of i18next (which as far as I know does not change when the language changes);
Maybe there is a point 4 and even a point 5, of which I am not aware of for now.
What is the current best practice for reusable React components which support formatting and i18n/l10n?
Tips and tricks on how to organize formatting/i18n/l10n code and separate these concerns from reusable components will be appreciated.
Thank you for the attention.
How would I tackle the implementation of internationalized ReactiveSearch DateRange component? I need a custom date format ("dd.mm.yyyy") and translated Names of months.
<ReactiveBase
app="carstore-dataset"
credentials="4HWI27QmA:58c731f7-79ab-4f55-a590-7e15c7e36721">
<DateRange
componentId="DateSensor"
dataField="mtime"
title="DateRange"
defaultValue={{
start: new Date('2019-04-01'),
end: new Date('2019-04-07')
}}
placeholder={{
start: 'Start Date',
end: 'End Date'
}}
focused={true}
numberOfMonths={2}
queryFormat="date"
autoFocusEnd={true}
showClear={true}
showFilter={true}
filterLabel="Date"
URLParams={false}
/>
<div>
Hello ReactiveSearch!
</div>
</ReactiveBase>
Thanks.
Sorry for the delay in responding. DateRange internally uses DayPickerInput from react-day-picker and Date Component have a prop called dayPickerInputProps you can directly use it to send props directly to the internal component.
To format the UI Date you will need to use additional package and props. You can go over to this page: https://react-day-picker.js.org/docs/input/ and scroll to section called Change Date Format to understand better.
Here is a sample code snippet to format the UI:
Packages Required:
import dateFnsFormat from "date-fns/format";
import dateFnsParse from "date-fns/parse";
import { DateUtils } from "react-day-picker";
Code
function parseDate(str, format, locale) {
const parsed = dateFnsParse(str, format, { locale });
if (DateUtils.isDate(parsed)) {
return parsed;
}
return undefined;
}
function formatDate(date, format, locale) {
return dateFnsFormat(date, format, { locale });
}
const FORMAT = "dd/MM/yyyy"; // Your Format
<DateRange
componentId="DateSensor"
dayPickerInputProps={{
formatDate,
format: FORMAT,
parseDate
}}
dataField="date_from"
/>
You can check a working example over here: https://codesandbox.io/s/daterange-9qfvo
Hope this Helps!
I'm trying to use this Bootstrap Datetime Picker (version 4.17.37) inside a Backgrid cell, but I'm having a problem with the way the widget is rendering. The widget appears as a thin popup right below the cell, but seems to have no content inside. However, if you log the widget's inner html to the console, the content is there. It seems to me that there is something unusual going on inside the place function of the datetime picker, shown here.
This is my code for the custom Backgrid cell and its editor.
// Editor for the datetime picker cell.
var DatetimePickerCellEditor = Backgrid.InputCellEditor.extend({
events: {},
initialize: function() {
Backgrid.InputCellEditor.prototype.initialize.apply(this, arguments);
var input = this;
var timezone = 'America/Lima',
format = 'YYYY-MM-DD HH:mm';
this.$el.datetimepicker({
format: format,
timeZone: timezone
}).on('dp.show', function(event) {
event.preventDefault();
var column = input.column.get('name'),
date = input.model.get(column);
input.$el.data("DateTimePicker").date(date);
}).on('dp.hide', function(event) {
event.preventDefault();
var dateObj = event.date,
dateLocal = null;
if (dateObj) {
var dateString = dateObj.format(format);
dateLocal = moment.tz(dateString, timezone);
}
var command = new Backgrid.Command({}),
column = input.column.get("name");
// Save date in model as string in local time.
input.model.set(column, dateLocal.format());
input.model.trigger("backgrid:edited",
input.model,
input.column,
command);
command = input = null;
}).on('dp.error', function(error) {
console.log(error)
});
}
});
// Cell to display the datetime picker.
var DatetimePickerCell = Backgrid.Cell.extend({
editor: DatetimePickerCellEditor
});
This produces the following result:
Upon inspection, the element that the datetime picker was using as a parent is an ancestor <div class="col-lg-12 col-md-12">. I then tried to solve the problem by giving the cell a { position: relative } CSS property. That indeed made the datetime picker use the cell as a parent, but produced the following result visually:
You can see that the picker appears to be below the cell, but is not visible.
Playing around with other properties, such as widgetParent and widgetPositioning gave similar results.
I had same problem.
(I know that this is an old question, but I use backbone/backgrid today. I have a lot of additional issues with angular, vue or react on data grids, I find that this combo is the best, and simplest, until these days).
well, You only must add this after backbone/backgrid css to your th, td containers.
.backgrid th,
.backgrid td {
display: table-cell;
position: relative;
overflow: visible;
}
I use it in an backbone/backgrid project and it works perfect.
It's incredible, there is and display: none style overrrided, but, for any reason it continues makeing this fail.
I hope this could help some one else.
one capture: