Code samples from the Flight Booking APP
Booking App DEMOThe Booking App contains a lot more files
List of sampled files, was chosen in random order
- Two files in .ts and .tsx to show TypeScript ( React JSX + TypeScript = TSX files )
- src/components/forms/GetMyBookings/index.tsx
- src/components/forms/utils/FormHelpers.ts
- Two .d.ts files to show types definition
- src/components/MicroReview/*.d.ts
- src/components/GreatFilter/*.d.ts
- Two .scss files to show styling
- src/components/Navigation/component.scss
- src/components/Footer/component.scss
File written in TypeScript + ReactJSX → .tsx → src/components/forms/GetMyBookings/index.tsx
import * as React from 'react'
import FormsHelpers from '../utils/FormHelpers'
import '../all.scss'
import './component.scss'
/**
* Your Bookings form
*/
export default class GetMyBookings extends React.Component<GetMyBookingsProps, GetMyBookingsState> {
// Important - we provide Default values for State to work with,
// Better to make Deep Copy every time we use data from initialState.
private initialState: GetMyBookingsState = {
cardSelected: 'getBookings',
forms: {
getBookings: [{ name: 'bookingNumber', error: true }, { name: 'emailBn', error: true }],
},
}
public constructor(props: GetMyBookingsProps) {
super(props)
this.state = JSON.parse(JSON.stringify(this.initialState)) // shallow copy
}
private helpers = new FormsHelpers(this.initialState)
public render = (): JSX.Element => {
return (
<>
<div className="get_bookings">
<div className="body outset">
<div className="inset">
<div className="form outset">
<div className="inset">
<div className="field_block">
<label>Booking number</label>
<div className="field_block_holder">
<input
type="input"
className="field_input password_field"
name="bookingNumber"
onChange={(e): any => this.helpers.validateField(e)}
onBlur={(e): any => this.helpers.validateField(e)}
/>
</div>
</div>
<div className="field_block">
<label>Email address</label>
<div className="field_block_holder">
<input
type="email"
className="field_input email_field"
name="emailBn"
onChange={(e): any => this.helpers.validateField(e)}
onBlur={(e): any => this.helpers.validateField(e)}
/>
</div>
</div>
</div>
</div>
<button
type="button"
className="login_button"
onClick={(): void =>
this.helpers.submitAction('/get_my_bookings', 'Email and Password Sign In')
}
>
<div className="inset">
<span>Retrieve booking</span>
</div>
</button>
</div>
</div>
</div>
</>
)
}
}
File written in TypeScript → src/components/forms/utils/FormHelpers.ts
import FieldValidation from 'Components/utils/FieldValidation'
/**
* Forms handling for validation, submit, reset forms etc...
*/
export default class FormsHelpers {
// Initial State is our data to describe fields in form
private initialState: any
private state: any
public constructor(initialState: any) {
this.initialState = initialState
this.state = JSON.parse(JSON.stringify(this.initialState)) // shallow copy
this.validateField = this.validateField.bind(this)
}
// Reset state
public resetState = (): any => {
// We need to Reset State forms here while we switch cards
this.state.forms = JSON.parse(JSON.stringify(this.initialState.forms)) // Replace Forms object with default data, we reset portion of State
}
// Select form
public selectForm = (key: string): any => {
this.state.cardSelected = key
}
// Submit Buttons action
public submitAction = (url: string, title: string): void => {
console.log(this.state)
// Get all Fields necessary to Fill and validate them if all are valid than submit
// Check selected Card or Tab and get Fields from State
const fields: any = this.state.forms[this.state.cardSelected]
let formValidationError: any = false // We need to use 'ANY' type here so TS compiler will not complain about "formValidationError === true ?" below
fields.map((el: any, i: number): any => {
el.error === true
? ((): any => {
formValidationError = true
// Mark all fields with error in State forms
let inputField = document.getElementsByName(el.name)[0]
inputField.className = inputField.className + ' error'
})()
: null
})
// If fields in form have Error we need to block submit
formValidationError === true ? null : this.openButtonLink(url, title)
}
// Open link in primitive new Window
public openButtonLink = (url: string, title: string): void => {
const h = 400
const w = 300
// Fix fot 2 screen position ( browsers + Firefox)
const twoScreensTop: number = window.screenTop != undefined ? window.screenTop : window.screenY
const twoScreensLeft: number = window.screenLeft != undefined ? window.screenLeft : window.screenX
const height: number = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height
const width: number = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width
const getZoom: number = width / window.screen.availWidth
const top: number = (height - h) / 4 / getZoom + twoScreensTop // standard is "2", but for better centering we need "4"
const left: number = (width - w) / 2 / getZoom + twoScreensLeft
const newWindow: Window | null = window.open(
url,
title,
'scrollbars=yes, width=' + w / getZoom + ', height=' + h / getZoom + ', top=' + top + ', left=' + left,
)
// Puts focus on the newWindow
if (window.focus)
((): any => {
return newWindow
})().focus()
}
// validate field and change View
public validateField = (e: React.ChangeEvent<HTMLInputElement>): boolean => {
const validate = new FieldValidation()
// Get Data Once
const fullFieldValidation: any = validate.inputField(e)
const fieldIsEmpty: boolean = fullFieldValidation.empty
// Important to declare e.currentTarget.parentNode.parentNode as ((): any => e.currentTarget.parentNode)().parentNode
// it prevents TS from complaining without need to Disable check for Null
const fieldHolderBlock: any = ((): any => e.currentTarget.parentNode)().parentNode
const fieldType: string = e.currentTarget.type
const fieldName: string = e.currentTarget.name
const minFillQuota: string = fullFieldValidation.minFillQuota
const correctEmail: string = fullFieldValidation.correct
// Add Visual markers and Content to field
// for Minimum input characters number for Password field
let errorText: any = 'This field can't be blank'
// if we need Error message
let errorMsg = false
// special test for password Fill Quota
fieldIsEmpty
? (errorMsg = true)
: fieldType == 'password'
? minFillQuota
? null
: ((): any => {
errorMsg = true
errorText = 'Must contain at least 8 characters'
})()
: null
// special test for email format
fieldIsEmpty
? (errorMsg = true)
: fieldType == 'email'
? correctEmail
? null
: ((): any => {
errorMsg = true
errorText = 'Invalid email format'
})()
: null
// if Field is Empty
// Mark Input Field
errorMsg
? // add Class to Change Field style
e.currentTarget.classList.add('error')
: // remove Class
e.currentTarget.classList.remove('error')
// Add Error message below
let errorHTML: any = document.createElement('span')
errorHTML.className = 'field_error_msg show'
errorHTML.innerHTML = errorText
// Clear Previous Error Span if exist
fieldHolderBlock.getElementsByClassName('field_error_msg')[0]
? fieldHolderBlock.getElementsByClassName('field_error_msg')[0].remove()
: null
// add Error Span
errorMsg
? // add HTML Span with error text
fieldHolderBlock.appendChild(errorHTML)
: // just Do not act (since All Error Span elements was cleared before )
null
// Important, put Data about validation to State
let fields: any = this.state.forms[this.state.cardSelected]
fields.map((el: any, i: number): any => {
// match by field type and mark with error if validation not passed
el.name == fieldName ? (fields[i].error = errorMsg) : null
})
// Proper setup of specific object in State
// this.setState({ forms: { [this.state.cardSelected]: fields } }, (): any => console.log(this.state)) // remove Other data in array, bad case
this.state.forms[this.state.cardSelected] = fields
return errorMsg
}
}
Types definition file → src/components/MicroReview/*.d.ts
interface MicroReviewState {
activeBodyTab: string
reviewData: {
id: string | number
key: string
name: string
logo: string
place: string | number
rating: string | number
ratingInFive: string | number
feedbackCount: string | number
votingStartDate: Date | string
clientsVoteCount: string | number
paramEval: string | number
list: {
id: string | number
categoryName: string
list: {
id: string | number
name: string
rating: string | number
}[]
}[]
}
feedBackData: {
id: string | number
name: string
feedbackCount: string | number
list: {
id: string | number
name: string
date: Date | string
upVote: string | number
downVote: string | number
content: string
}[]
}
options: any
}
Types definition file → src/components/GreatFilter/*.d.ts
interface FilterSavedSearchState {
reRender: number
data: any
}
interface FilterSavedSearchProps {
data: any
callBackAction: (command: string, data: any) => void
}
interface GreatFilterViewProps {
state: {
screenWidth: number
flightForm: {
oneWay: boolean
roundTrip: boolean
multiCity: boolean
}
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
passTypePicker: boolean
}
fieldsInput: {
locationFrom: {
name: string
code: string
error: boolean
}
locationTo: {
name: string
code: string
error: boolean
}
dateFrom: {
date: Date | any
dateFull: Date | any
error: boolean
}
dateTo: {
date: Date | any
dateFull: Date | any
error: boolean
}
pass: {
combinedField: string
adultsPass: number
childrenPass: number
infantsPass: number
passType: {
any: boolean
business: boolean
economy: boolean
}
}
}
fieldsMultiSets: {
setI: {
visible: boolean
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
}
fieldsInput: any
}
setII: {
visible: boolean
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
}
fieldsInput: any
}
}
savedSearch: any
locations: {
from: {
name: string
code: string
}
to: {
name: string
code: string
}
}
}
calls: {
saveStateByPropCallBack: (object: any, value: any) => void
flightFormSwitch: (formKey: string) => void
dateSelect: (from: Date, to: Date, fieldSetKey: string) => void
handleClickDate: (fieldKey: string, fieldSetKey: string) => void
closeDateSelectBlock: (fieldKey: string, fieldSetKey: string) => void
handleChangeLocation: (e: React.FormEvent<HTMLInputElement>, fieldKey: string, fieldSetKey: string) => void
selectLocation: (
fieldKey: string,
locationName?: string,
locationCode?: string,
command?: string,
fieldSetKey?: string,
) => any
openPassTypeBlock: () => void
handleChange: (e?: React.FormEvent<HTMLInputElement>, fieldKey?: string) => void
swapLocations: (fieldSetKey: string) => void
passTypeCallBack: (a: any) => void
searchTickets: () => void
useSavedSearchCallBack: (command: string, data: any) => void
}
refs: {
locationSelectBlockRef: React.RefObject<HTMLDivElement> | any
dateSelectBlockRef: React.RefObject<HTMLDivElement> | any
locationFromInputRef: React.RefObject<HTMLInputElement> | any
locationToInputRef: React.RefObject<HTMLInputElement> | any
}
inputFieldDate?: string
searchRequest: string
}
interface GreatFilterViewState {
switchers: {
datePicker: boolean
}
}
interface GreatFilterState {
_isMounted: boolean
screenWidth: number
flightForm: {
oneWay: boolean
roundTrip: boolean
multiCity: boolean
}
fieldsInput: {
locationFrom: {
name: string
code: string
error: boolean
}
locationTo: {
name: string
code: string
error: boolean
}
dateFrom: {
date: string
dateFull: Date | any
error: boolean
}
dateTo: {
date: string
dateFull: Date | any
error: boolean
}
pass: {
combinedField: string
adultsPass: number
childrenPass: number
infantsPass: number
passType: {
any: boolean
business: boolean
economy: boolean
}
}
}
fieldsMultiSets: {
setI: {
visible: boolean
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
}
fieldsInput: any
}
setII: {
visible: boolean
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
}
fieldsInput: any
}
}
switchers: {
datePicker: boolean
locationPickerFrom: boolean
locationPickerTo: boolean
passTypePicker: boolean
}
savedSearch: any
locations: {
from: {
name: string
code: string
}
to: {
name: string
code: string
}
}
gMaps: any
error: any
}
interface PassTypeProps {
pass: {
combinedField: string
adultsPass: number
childrenPass: number
infantsPass: number
passType: {
any: boolean
business: boolean
economy: boolean
}
}
callBackAction: (a: any) => void
}
interface PassTypeState {
pass: {
combinedField: string
adultsPass: number
childrenPass: number
infantsPass: number
passType: {
any: boolean
business: boolean
economy: boolean
}
}
activationMarkers: {
passTypeBoxActive: boolean
}
}
interface PlacesListProps {
searchRequest: string
fieldKey: string
fieldSetKey: string
callBackAction: (
fieldKey: string,
locationName: string,
locationCode: string,
command?: string,
fieldSetKey?: string,
) => void
}
interface PlacesListState {
placesSearching: boolean
search: {
request: string
data: { [key: string]: RefinedAirportsList['key'] } | object
}
}
interface RefinedAirportsList {
[key: string]: {
seo: string
desc: string
code: string
name: string
city: string
airports: {
code: string
name: string
}[]
}
}
interface SuggestedCitiesProps {
switch: string
selectLocation: () => {}
}
interface CountryObj {
codeI: string
codeNATO: string
name: string
codeISO: string
}
type CountriesList = CountryObj[]
Scss styling file → src/components/Navigation/component.scss
#navigation {
/* background-color: #dbf0ff; */
color: #1a1a1a;
font-family: BlinkMacSystemFont, -apple-system, Helvetica, Arial, sans-serif;
display: table;
width: 100%;
border-bottom: 1px solid #d1d8e3;
}
.navigation_block {
&.navigation_holder {
max-width: 1180px;
margin: 0 auto;
display: table;
width: 100%;
}
}
.logo_holder {
display: block;
float: left;
height: 62px;
margin-right: 20px;
img.logo {
display: block;
width: 101px;
margin-top: 6px;
margin-left: 11px;
margin-right: 20px;
cursor: pointer;
}
}
.menu_holder_outset {
max-height: 62px;
overflow: hidden;
float: left;
display: table;
width: 67%;
.menu_holder_inset {
display: table;
}
.menu_item_holder {
display: inline-block;
float: none;
margin-right: -4px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.menu_item {
margin: 0 30px 0 0;
position: relative;
font-size: 16px;
padding: 0;
line-height: 69px;
height: 59px;
display: block;
border-bottom: 3px solid transparent;
color: #333;
text-decoration: none;
&.active {
color: #057ec3;
border-color: #057ec3;
}
&:hover {
color: #2681ff;
}
}
}
}
.basket_holder_outset {
height: 62px;
overflow: hidden;
float: right;
display: table;
margin-left: -200px;
.basket_holder {
margin-right: 10px;
.basket_holder_inset {
margin-top: 16px;
font-size: 14px;
line-height: 30px;
padding: 0 15px;
background-color: #fff;
display: inline-block;
color: #057ec3;
border-radius: 4px;
-webkit-border-radius: 4px;
cursor: pointer;
border: 1px dashed #057ec3;
border: 1px dashed #d1d8e3;
&:hover {
border: 1px dashed #057ec3;
}
.basket_icon {
display: block;
width: 120px;
height: 55px;
position: absolute;
background: transparent url(~Imgs/airplane-ticket-png-2-300x200.png) no-repeat 0px 1px;
background-size: 77px;
margin-left: -107px;
margin-top: -10px;
}
span.basket_ticket_counter {
font-weight: bold;
margin-left: 7px;
}
}
}
}
@media (max-width: 740px) {
.app .basket_holder_outset .basket_holder .basket_holder_inset .basket_icon {
width: 16px;
}
}
@media (max-width: 570px) {
.app .navigation_block .logo_holder {
display: block;
float: none;
height: 62px;
margin-right: 0;
background: #ffffff00;
padding-top: 10px;
width: auto;
position: absolute;
overflow: hidden;
right: 0px;
top: 52px;
}
.app .navigation_block .logo_holder img.logo {
width: 110px;
margin-right: -11px;
}
.app .basket_holder_outset .basket_holder .basket_holder_inset .basket_icon {
width: 120px;
}
}
Scss styling file → src/components/Footer/component.scss
.main_footer {
display: table;
width: 100%;
min-height: 200px;
background-color: #ffffff;
margin-top: 0px;
padding-bottom: 30px;
.main_footer_inset {
width: 100%;
max-width: 1180px;
position: relative;
margin-left: auto;
margin-right: auto;
padding-left: 10px;
padding-right: 10px;
clear: both;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
ul.navigation_list {
position: relative;
display: -webkit-flex;
display: -ms-flex;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
padding: 20px 0px;
list-style: none;
padding-top: 20px;
padding-bottom: 20px;
margin: 0px;
}
li.navigation_item {
position: relative;
float: left;
margin-right: 35px;
flex-grow: 1;
margin-right: 10px;
margin-bottom: 0;
width: auto;
min-width: 1px;
font-size: 14px;
&.last_item {
width: 0px;
text-align: right;
margin-right: 0px;
}
}
li.navigation_item span.item_text {
cursor: pointer;
}
li.navigation_item span.item_text:hover {
color: #3c84ac;
}
.footer_info_block.i .footer_info_block_inset {
width: 100%;
max-width: 1180px;
position: relative;
margin-left: auto;
margin-right: auto;
padding-left: 10px;
padding-right: 10px;
clear: both;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
color: #3b3b3b;
background-color: #ced5e2;
}
.footer_divider_line {
max-width: 1180px;
margin: 0 auto;
border-bottom: 1px solid rgba(171, 186, 212, 0.4);
}
.footer_info_block {
margin-top: 30px;
font-size: 14px;
line-height: 40px;
color: #1c1c47;
text-align: center;
max-width: 1180px;
margin: 0 auto;
display: table;
width: 100%;
}
.footer_info_block.ii {
order: 5;
line-height: 20px;
margin: 10px auto 0;
flex-basis: 100%;
text-align: left;
span {
font-size: 13px;
color: #3a3a3a;
line-height: 20px;
}
}
.footer_info_block.iii {
text-align: left;
margin-top: 20px;
}
.footer_info_block.iii .footer_column {
display: inline-block;
width: 33.3%;
}
.footer_social_links svg {
width: 30px;
height: 30px;
fill: #aebad2;
cursor: pointer;
}
.footer_social_links {
text-align: right;
margin-right: 0px;
}
.footer_social_links span:hover {
transition: background 0.3s;
}
.footer_social_links span:hover svg {
fill: transparent;
}
.footer_social_links span.f_icon:hover {
background: #ffffff;
}
.footer_social_links span.f_icon:hover svg {
fill: #4267b2;
}
.footer_social_links span.i_icon:hover svg {
fill: #fb9235;
}
.footer_social_links span.v_icon:hover svg {
fill: #665cac;
}
.footer_social_links span.t_icon:hover {
background: linear-gradient(203.2deg, #37aee2 0%, #1e96c8 100%);
}
.footer_social_links span {
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
width: 30px;
height: 30px;
display: inline-block;
margin-left: 10px;
}
.footer_info_block.iv {
margin-top: 30px;
font-size: 14px;
line-height: 40px;
color: #1c1c47;
text-align: left;
max-width: 1180px;
margin: 0 auto;
margin-top: 20px;
display: table;
width: 100%;
}
.footer_info_block.iv .footer_column {
display: inline-block;
width: 33.3%;
text-align: center;
}
span.footer_logo_block.american_express img {
height: 50px;
float: right;
margin-right: -9px;
}
span.footer_logo_block.visa img {
width: 100px;
float: left;
padding-bottom: 8px;
}
span.footer_logo_block.master_card img {
width: 185px;
padding-top: 0px;
}
span.footer_logo_block.master_card {
text-align: center;
}
.footer_info_block.v {
padding: 35px 0;
border-top: 1px solid rgba(171, 186, 212, 0.4);
border-bottom: 1px solid rgba(171, 186, 212, 0.4);
}
.footer_info_block.v .footer_column {
display: inline-block;
width: 20%;
text-align: left;
}
.footer_info_block.v .footer_column.airlines_column {
width: 10%;
}
.footer_info_block.v .footer_column.airports_column {
width: 25%;
}
.footer_info_block.v .footer_column.routes_column {
width: 25%;
}
ul.footer_list {
display: -webkit-flex;
display: -ms-flex;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
flex-grow: 1;
flex-basis: 16.66666667%;
font-size: 14px;
line-height: 2.1;
max-width: 230px;
padding-top: 10px;
padding-right: 0px;
padding-left: 0px;
box-sizing: border-box;
-webkit-transition: 0.3s;
-moz-transition: 0.3s;
-ms-transition: 0.3s;
-o-transition: 0.3s;
transition: 0.3s;
}
li.item_head {
display: -webkit-flex;
display: -ms-flex;
display: flex;
font-size: 16px;
font-weight: 700;
line-height: 1.8;
margin-bottom: 10px;
align-items: center;
min-height: 34px;
}
li.item {
display: -webkit-flex;
display: -ms-flex;
display: flex;
-ms-align-items: center;
align-items: center;
min-height: 34px;
cursor: pointer;
&:hover {
color: #4990e2;
-webkit-transition: 0.2s;
-moz-transition: 0.2s;
-ms-transition: 0.2s;
-o-transition: 0.2s;
transition: 0.2s;
}
}
li.item_bundle_link span:after {
content: '→';
position: absolute;
font-size: 22px;
color: #4990e2;
transform: translateY(-50%);
height: 20px;
margin-left: 7px;
}
li.item.item_bundle_link {
display: -webkit-flex;
display: -ms-flex;
display: flex;
-ms-align-items: center;
align-items: center;
min-height: 34px;
color: #4990e2;
}
}
}
@media (max-width: 900px) {
.main_footer {
.main_footer_inset {
.footer_info_block.v {
.footer_column {
width: 33% !important;
float: left;
}
}
li.navigation_item {
&.last_item {
width: auto;
text-align: right;
margin-right: 0px;
white-space: nowrap;
}
}
}
}
}
@media (max-width: 570px) {
.main_footer {
.main_footer_inset {
.footer_info_block.i .footer_info_block_inset {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
line-height: 25px;
padding: 11px 0px;
strong {
background: #fff;
padding: 0px 7px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
box-shadow: 0px 0px 0px #000;
}
}
.footer_info_block.v {
padding: 40px 0px;
border-bottom: 0px;
padding-top: 0px;
margin-top: 20px;
.footer_column {
width: 100% !important;
border-bottom: 1px solid #dde3ee;
&.airlines_column {
// border: none;
}
.footer_list {
overflow: hidden;
padding: 0px;
margin: 0px;
height: auto;
max-width: 100%;
.item_head {
min-height: auto;
padding: 10px 0px 10px 0;
cursor: pointer;
width: 100%;
margin: 0px;
&:before {
content: 'i';
display: block;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
background: url(~Imgs/schevron_right.png) no-repeat center center;
background-size: 7px;
width: 30px;
height: 30px;
font-size: 0px;
position: absolute;
right: 13px;
}
}
.item {
display: none;
}
&.active {
li.item {
display: -webkit-flex;
display: -ms-flex;
display: flex;
}
.item_head {
&:before {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
}
}
}
}
}
span.footer_logo_block img {
zoom: 0.7;
}
li.navigation_item {
width: 30%;
margin-right: 4%;
line-height: 28px;
&.last_item {
width: 30%;
text-align: left;
margin-right: 4%;
}
}
.footer_info_block.iii {
text-align: center;
margin-top: 30px;
display: none;
.footer_column {
display: inline-block;
width: 100%;
}
}
.footer_social_links {
text-align: center;
margin-right: 0px;
span {
width: auto;
margin: 0 5px;
}
}
ul.navigation_list {
display: none;
}
}
}
}