lodash#reject TypeScript Examples
The following examples show how to use
lodash#reject.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: api.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
function filterBySettings(
suggestions: UI5XMLViewCompletion[],
settings: CodeAssistSettings
): UI5XMLViewCompletion[] {
let filteredSuggestions = suggestions;
if (!settings.codeAssist.deprecated) {
filteredSuggestions = reject(
filteredSuggestions,
(suggestion) =>
isUI5NodeXMLViewCompletion(suggestion) &&
suggestion.ui5Node.deprecatedInfo?.isDeprecated === true
);
}
if (!settings.codeAssist.experimental) {
filteredSuggestions = reject(
filteredSuggestions,
(suggestions) =>
isUI5NodeXMLViewCompletion(suggestions) &&
suggestions.ui5Node.experimentalInfo?.isExperimental === true
);
}
return filteredSuggestions;
}
Example #2
Source File: app.ts From PrettyScribe with ISC License | 6 votes |
function handleFileSelect(event: Event) {
let input;
let files;
if (event?.type === "resize") input = fileChangeEvent?.target as HTMLInputElement;
else input = event?.target as HTMLInputElement;
files = input?.files;
cleanup();
if (files) {
if (event?.type !== "resize") fileChangeEvent = event;
const reader = new FileReader();
reader.onerror = () => {
reader.abort();
reject(new DOMException('Failed to read roster file.'));
}
reader.onloadend = async () => {
const content = reader.result as string;
const xmldata = await unzip(content);
parseXML(xmldata);
}
reader.readAsBinaryString(files[0]);
}
}
Example #3
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
handleDelete = (uniKey: string) => {
const dataSource = [...this.state.dataSource];
const onDel = this.props.onDel || noop;
this.setState(
{
dataSource: reject(dataSource, { uniKey }),
},
() => {
onDel(Array.isArray(this.props.data) ? this.state.dataSource : this.getTableData());
},
);
};
Example #4
Source File: non-unique-id.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
function buildIssuesForSingleID(
duplicatedAttributes: DuplicatedIDXMLAttribute[],
id: string
): NonUniqueIDIssue[] {
const issuesForID = map(
duplicatedAttributes,
(currDupAttrib, currAttribIdx) => {
const currDupIdValToken = currDupAttrib.syntax.value;
// Related issues must not include the "main" issue attribute
const relatedOtherDupIDAttribs = reject(
duplicatedAttributes,
(_, arrIdx) => arrIdx === currAttribIdx
);
return {
kind: "NonUniqueIDIssue" as const,
message: buildMessage(NON_UNIQUE_ID.msg, id),
severity: "error" as const,
offsetRange: {
start: currDupIdValToken.startOffset,
end: currDupIdValToken.endOffset,
},
identicalIDsRanges: map(relatedOtherDupIDAttribs, (_) => ({
start: _.syntax.value.startOffset,
end: _.syntax.value.endOffset,
})),
};
}
);
return issuesForID;
}
Example #5
Source File: namespace.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
/**
* Suggests Namespaces inside Element
* For example xmlns:m should provide list of namespaces ending with .m.. (like "sap.m", "sap.ui.core.mvc")
* attribute is namespace attribute if it's equal to "xmlns" or starts with "xmlns:"
* in first case all possible namespaces of semantic module will be provided excluding pre-existing ones
*/
export function namespaceKeysSuggestions(
opts: UI5AttributeNameCompletionOptions
): UI5NamespacesInXMLAttributeKeyCompletion[] {
const ui5Model = opts.context;
const xmlElement = opts.element;
if (opts.prefix === undefined) {
return [];
}
const xmlnsPrefix = getXMLNamespaceKeyPrefix(opts.prefix);
if (xmlnsPrefix === undefined) {
return [];
}
const existingNamespacesAttributes: XMLAttribute[] = filter(
xmlElement.attributes,
isExistingNamespaceAttribute
);
const existingNamespacesNames = compact(
map(existingNamespacesAttributes, (_) => _.value)
);
const applicableNamespaces = filter(ui5Model.namespaces, (_) =>
isNamespaceApplicable(_, xmlnsPrefix)
);
const suggestedNamespaces = reject(applicableNamespaces, (_) =>
includes(existingNamespacesNames, ui5NodeToFQN(_))
);
return map(suggestedNamespaces, (_) => ({
type: "UI5NamespacesInXMLAttributeKey",
ui5Node: _,
astNode: opts.attribute as XMLAttribute,
}));
}
Example #6
Source File: turn.ts From fishbowl with MIT License | 5 votes |
export function drawableCardsWithoutCompletedCardsInActiveTurn(
cards: CurrentGameSubscription["games"][0]["cards"],
completedCardIdsInActiveTurn: Array<number>
) {
return reject(cards, (card) => completedCardIdsInActiveTurn.includes(card.id))
}
Example #7
Source File: filter-members.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
export function filterMembersByNames<T extends WithName>(
members: T[],
preExistingNames: string[]
): T[] {
return reject(members, (_) => includes(preExistingNames, _.name));
}
Example #8
Source File: aggregation.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
/**
* Suggests Aggregation inside sap.ui.core.Element
* For example: 'content' and 'footer' inside `sap.m.Page`
*/
export function aggregationSuggestions(
opts: UI5ElementNameCompletionOptions
): UI5AggregationsInXMLTagNameCompletion[] {
const ui5Model = opts.context;
const prefix = opts.prefix ?? "";
const xmlElement = opts.element;
const parentXMLElement = xmlElement.parent;
// The top level element cannot be an aggregation
if (parentXMLElement.type === "XMLDocument") {
return [];
}
const prefixParts = splitQNameByNamespace(prefix);
// An aggregation must be on the parent tag namespace.
// We suggest the completion even if the namespace was not defined.
if (
prefixParts.prefix !== undefined &&
!isSameXMLNSFromPrefix(
parentXMLElement.ns,
parentXMLElement,
prefixParts.prefix,
xmlElement
)
) {
return [];
}
const parentUI5Class = getUI5ClassByXMLElement(parentXMLElement, ui5Model);
if (!isElementSubClass(parentUI5Class)) {
return [];
}
const existingAggregations = compact(
uniq(map(parentXMLElement.subElements, (_) => _.name))
);
const existingAggregationsWithoutCurrent =
xmlElement.name === null
? existingAggregations
: reject(existingAggregations, (name) => name === xmlElement.name);
const uniquePrefixMatchingAggregations = filterMembersForSuggestion(
flattenAggregations(parentUI5Class),
prefixParts.localName,
existingAggregationsWithoutCurrent
);
return map(uniquePrefixMatchingAggregations, (_) => ({
type: "UI5AggregationsInXMLTagName",
ui5Node: _,
astNode: xmlElement,
}));
}
Example #9
Source File: prop-event-assoc.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
/**
* Suggests Properties and Events inside Element
* For example: 'backgroundDesign' and 'icon' in `sap.m.Page` element
*/
export function propEventAssocSuggestions(
opts: UI5AttributeNameCompletionOptions
): PropEventsAssocInXMLAttributeKeyCompletion[] {
const ui5Model = opts.context;
const xmlElement = opts.element;
const elementClass = getUI5ClassByXMLElement(xmlElement, ui5Model);
if (!isElementSubClass(elementClass)) {
return [];
}
const allProps: (UI5Prop | UI5Event | UI5Association)[] = flattenProperties(
elementClass
);
const allEvents = flattenEvents(elementClass);
const allAssociations = flattenAssociations(elementClass);
const allPropertiesEventsAssociations = allProps
.concat(allEvents)
.concat(allAssociations);
const prefix = opts.prefix ?? "";
const existingAttributeNames = compact(
uniq(map(xmlElement.attributes, (_) => _.key))
);
const currentAttributeKey = opts.attribute?.key;
const existingAttributeNamesWithoutCurrent =
currentAttributeKey === undefined
? existingAttributeNames
: reject(existingAttributeNames, (name) => name === currentAttributeKey);
const uniquePrefixMatchingAttributes = filterMembersForSuggestion(
allPropertiesEventsAssociations,
prefix,
existingAttributeNamesWithoutCurrent
);
const suggestions = map(uniquePrefixMatchingAttributes, (_) => ({
type: NodeKindToSuggestionType[_.kind],
ui5Node: _,
astNode:
(opts.attribute as XMLAttribute) ?? createDummyAttribute(xmlElement),
}));
// Using casing due to dynamic assignment to the `type` property of each suggestion
return suggestions as PropEventsAssocInXMLAttributeKeyCompletion[];
}
Example #10
Source File: WaitingForSubmissions.tsx From fishbowl with MIT License | 4 votes |
function WaitingForSubmissions() {
const { t } = useTranslation()
const currentGame = React.useContext(CurrentGameContext)
const currentPlayer = React.useContext(CurrentPlayerContext)
const numEntriesPerPlayer = currentGame.num_entries_per_player
const numPlayers = currentGame.players.length
if (!numEntriesPerPlayer || !numPlayers) {
return null
}
const total = numEntriesPerPlayer * numPlayers
const submittedOrAcceptedSoFar = currentGame.cards.length
const waitingForPlayers = reject(currentGame.players, (player) => {
return (
filter(currentGame.cards, (card) => card.player_id === player.id)
.length === numEntriesPerPlayer
)
})
if (!submittedOrAcceptedSoFar) {
return null
}
let state: WaitingForSubmissionsState
if (total === 0 || submittedOrAcceptedSoFar !== total) {
state = WaitingForSubmissionsState.Waiting
} else if (PlayerRole.Host === currentPlayer.role) {
state = WaitingForSubmissionsState.SubmittedAssign
} else {
state = WaitingForSubmissionsState.SubmittedWait
}
const unscreenedCards =
PlayerRole.Host === currentPlayer.role && currentGame.screen_cards
? currentGame.cards.filter(
(card) =>
card.is_allowed === null && card.player_id !== currentPlayer.id
)
: []
return (
<>
<Grid item>
<Title text={t("cardSubmission.waiting.title", "Well done!")} />
</Grid>
{
{
[WaitingForSubmissionsState.Waiting]: (
<>
<Grid item container justify="center">
{t(
"cardSubmission.waiting.description",
"Just waiting for everyone else..."
)}
<div style={{ width: 4 }} />
{waitingForPlayers.map((player) => (
<>
<PlayerChip username={player.username || ""} />
<div style={{ width: 4 }} />
</>
))}
</Grid>
<Grid item>
<Typography variant="h5">
{t(
"cardSubmission.waiting.progress",
"{{ progress }} cards so far",
{
progress: `${submittedOrAcceptedSoFar}/${total}`,
}
)}
</Typography>
</Grid>
</>
),
[WaitingForSubmissionsState.SubmittedAssign]: (
<Grid item>
{t(
"cardSubmission.waiting.submitted.host",
"All players submitted! As the host, you can now assign teams."
)}
</Grid>
),
[WaitingForSubmissionsState.SubmittedWait]: (
<Grid item>
{t(
"cardSubmission.waiting.submitted.player",
"All players submitted {{ cardCount }} cards in total. Now we are waiting on the host to start the game!",
{ cardCount: submittedOrAcceptedSoFar }
)}
</Grid>
),
}[state]
}
{PlayerRole.Host === currentPlayer.role && currentGame.screen_cards && (
<Grid item container direction="column" spacing={2} alignItems="center">
{unscreenedCards.map((card, index) => (
<Grid item key={index}>
<ScreenCard card={card} />
</Grid>
))}
</Grid>
)}
{WaitingForSubmissionsState.SubmittedAssign === state ? (
<div style={{ marginTop: 30 }}>
<AssignTeamsButton />
</div>
) : !unscreenedCards.length ? (
<div style={{ marginTop: 50 }}>
<Fishbowl />
</div>
) : null}
</>
)
}
Example #11
Source File: index.tsx From fishbowl with MIT License | 4 votes |
function EndGame() {
const { t } = useTranslation()
const currentGame = React.useContext(CurrentGameContext)
const currentPlayer = React.useContext(CurrentPlayerContext)
const titleClasses = useTitleStyle()
const [redirectHome, setRedirectHome] = React.useState(false)
const turnsByPlayer = new Map()
currentGame.turns.forEach((turn) => {
turnsByPlayer.set(
turn.player_id,
reject(
(turnsByPlayer.get(turn.player_id) || []).concat([
turn.completed_card_ids,
]),
(arr) => isEmpty(arr)
)
)
})
const scoresByPlayer = new Map()
turnsByPlayer.forEach((value, key) => {
scoresByPlayer.set(key, flatMap(value).length)
})
let highScore = -1
scoresByPlayer.forEach((value, key) => {
if (value > highScore) {
highScore = value
}
})
const highScorePlayers = filter(
currentGame.players,
(player) => scoresByPlayer.get(player.id) === highScore
)
const redScore = teamScore(Team.Red, currentGame.turns, currentGame.players)
const blueScore = teamScore(Team.Blue, currentGame.turns, currentGame.players)
const tie = redScore === blueScore
const winningTeam = redScore > blueScore ? Team.Red : Team.Blue
const shareContent = t(
"end.shareContent",
"Just had a great time playing {{ url }} online, you should check it out!",
{ url: "fishbowl-game.com" }
)
return (
<>
{redirectHome && <Redirect to={routes.root}></Redirect>}
<Grid container direction="column" spacing={2}>
<Grid item style={{ textAlign: "center" }}>
<Typography variant="h4" className={titleClasses.title}>
{t("end.title", "Game Over")}
</Typography>
</Grid>
<Grid item style={{ textAlign: "center" }}>
<Box style={{ fontSize: "24px", lineHeight: "0.9" }}>
{<span style={{ color: TeamColor[Team.Red] }}>{redScore}</span>}
{" - "}
{<span style={{ color: TeamColor[Team.Blue] }}>{blueScore}</span>}
</Box>
</Grid>
<Grid item>
<Divider variant="fullWidth"></Divider>
</Grid>
<Grid item>
{tie
? t("end.result.tie", "It's a tie! Play again to break it.")
: t("end.result.win", "{{ teamName }} wins! Bask in the glory.", {
teamName: t(
`teams.teamName.${winningTeam}`,
`Team ${winningTeam}`
).toLocaleUpperCase(),
})}
</Grid>
{!isEmpty(highScorePlayers) && (
<Grid item>
<Trans
t={t}
i18nKey="end.highScore"
count={highScorePlayers.length}
values={{ highScore }}
tOptions={{
defaultValue_plural:
"<0>{{playerUsernames}}</0> put the team on their back. They got their team to guess the most number of cards ({{ highScore }}!), across all rounds.",
}}
>
<PlayerChipList players={highScorePlayers}>
{{ playerUsernames: null }}
</PlayerChipList>
{
" put the team on their back. They got their team to guess the most number of cards ({{ highScore }}!), across all rounds."
}
</Trans>
</Grid>
)}
<Grid item>
{t("end.yourScore", "You scored {{ score }} across all rounds.", {
score: scoresByPlayer.get(currentPlayer.id) || 0,
})}
</Grid>
<Grid item>
<Divider variant="fullWidth"></Divider>
</Grid>
<Grid
item
container
direction="column"
justify="center"
alignItems="center"
>
<Box pb={2}>
{t(
"end.thanks.share",
"Thanks for playing -- if you had fun, share it with your friends!"
)}
</Box>
<Grid container justify="center" spacing={2}>
<Grid item>
<TwitterShareButton
url={"fishbowl-game.com"}
title={shareContent}
>
<TwitterIcon size={50} round />
</TwitterShareButton>
</Grid>
<Grid item>
<FacebookShareButton
url={"fishbowl-game.com"}
title={shareContent}
>
<FacebookIcon size={50} round />
</FacebookShareButton>
</Grid>
</Grid>
<Trans t={t} i18nKey="end.thanks.support">
<Box pb={1} pt={2}>
Or support the project by
</Box>
<Box py={2}>
<BuyMeACoffeeButton>Buying us a coffee</BuyMeACoffeeButton>
</Box>
<Box py={1}>
<Link href="https://forms.gle/L9qWMsnAUghXqqxE9" target="_blank">
sharing your feedback
</Link>
, and playing again soon!
</Box>
</Trans>
</Grid>
<Grid item>
<Divider variant="fullWidth"></Divider>
</Grid>
<Grid item container justify="center">
<Box py={1}>
<Button variant="outlined" onClick={() => setRedirectHome(true)}>
{t("end.playAgainButton", "Play Again")}
</Button>
</Box>
</Grid>
</Grid>
</>
)
}
Example #12
Source File: ControllableRoundSettings.tsx From fishbowl with MIT License | 4 votes |
function ControllableRoundSettings() {
const currentGame = React.useContext(CurrentGameContext)
const [updateAllRounds] = useUpdateAllRoundsMutation()
const [deleteRound] = useDeleteRoundMutation()
const [addRound] = useAddRoundMutation()
const [showAddRoundForm, setShowAddRoundForm] = React.useState(false)
const [addRoundValue, setAddRoundValue] = React.useState("")
const ref = React.useRef<HTMLDivElement>(null)
useOnClickOutside(ref, () => {
if (showAddRoundForm) {
setShowAddRoundForm(false)
setAddRoundValue("")
}
})
return (
<div ref={ref}>
<RoundSettingsList>
<>
{currentGame.rounds.map((round, index) => {
return (
<ListItem key={round.id}>
<ListItemIcon>
<>
<IconButton
color="primary"
size="small"
disabled={index === 0}
onClick={() => {
const updatedRounds = arrayMove(
currentGame.rounds,
index,
index - 1
)
updateAllRounds({
variables: {
gameId: currentGame.id,
rounds: updatedRounds.map(
(updatedRound, updatedIndex) => {
return {
id: updatedRound.id,
value: updatedRound.value,
order_sequence: updatedIndex,
}
}
),
},
})
}}
>
<ArrowDropUpIcon></ArrowDropUpIcon>
</IconButton>
<IconButton
color="primary"
size="small"
disabled={index === currentGame.rounds.length - 1}
onClick={() => {
const updatedRounds = arrayMove(
currentGame.rounds,
index,
index + 1
)
updateAllRounds({
variables: {
gameId: currentGame.id,
rounds: updatedRounds.map(
(updatedRound, updatedIndex) => {
return {
id: updatedRound.id,
value: updatedRound.value,
order_sequence: updatedIndex,
}
}
),
},
})
}}
>
<ArrowDropDownIcon></ArrowDropDownIcon>
</IconButton>
</>
</ListItemIcon>
<ListItemText>
<Box pl={2}>
{index + 1}. {capitalize(round.value)}
</Box>
</ListItemText>
<ListItemSecondaryAction>
<IconButton
size="small"
onClick={async () => {
const updatedRounds = reject(
currentGame.rounds,
(_r) => _r.id === round.id
)
await deleteRound({
variables: {
id: round.id,
gameId: currentGame.id,
rounds: updatedRounds.map(
(updatedRound, updatedIndex) => {
return {
id: updatedRound.id,
value: updatedRound.value,
order_sequence: updatedIndex,
}
}
),
},
})
}}
>
<CloseIcon></CloseIcon>
</IconButton>
</ListItemSecondaryAction>
</ListItem>
)
})}
<ListItem>
<ListItemText style={{ paddingLeft: "76px" }}>
{showAddRoundForm ? (
<TextField
value={addRoundValue}
onChange={({ target: { value } }) => setAddRoundValue(value)}
size="small"
autoFocus
InputProps={{
startAdornment: (
<InputAdornment position="start">
{currentGame.rounds.length + 1}.
</InputAdornment>
),
}}
></TextField>
) : (
<Box color="#fafafa">.</Box>
)}
</ListItemText>
<ListItemSecondaryAction>
<IconButton
size="small"
disabled={showAddRoundForm && addRoundValue.length === 0}
color={addRoundValue.length > 0 ? "primary" : "default"}
onClick={() => {
if (showAddRoundForm && addRoundValue.length > 0) {
addRound({
variables: {
object: {
game_id: currentGame.id,
value: lowerFirst(addRoundValue),
order_sequence: currentGame.rounds.length,
},
},
})
setShowAddRoundForm(false)
setAddRoundValue("")
} else {
setShowAddRoundForm(true)
}
}}
>
<AddCircleIcon></AddCircleIcon>
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</>
</RoundSettingsList>
</div>
)
}
Example #13
Source File: WaitingRoom.tsx From fishbowl with MIT License | 4 votes |
function WaitingRoom(props: {
cardPlayStyle: GameCardPlayStyleEnum
wordList?: string
}) {
const { t } = useTranslation()
const MIN_NUMBER_OF_PLAYERS = 2 // TODO: Update to 4.
const currentGame = React.useContext(CurrentGameContext)
const currentPlayer = React.useContext(CurrentPlayerContext)
const [updateGameState] = useUpdateGameStateMutation()
const [removePlayer] = useRemovePlayerMutation()
const [loadWords] = useLoadWordsMutation()
const [updateAllPlayers] = useUpdateAllPlayersMutation()
const playersWithUsernames =
reject(currentGame.players, (player) => isEmpty(player.username)) || []
const playersWithoutUsernames =
filter(currentGame.players, (player) => isEmpty(player.username)) || []
const canSeeStartGameButton = currentPlayer.role === PlayerRole.Host
const canStartGame =
canSeeStartGameButton &&
currentGame.seconds_per_turn &&
find(playersWithUsernames, (player) => player.id === currentPlayer.id) &&
playersWithUsernames.length >= MIN_NUMBER_OF_PLAYERS &&
((props.cardPlayStyle === GameCardPlayStyleEnum.PlayersSubmitWords &&
currentGame.num_entries_per_player) ||
(props.cardPlayStyle === GameCardPlayStyleEnum.HostProvidesWords &&
Boolean(props.wordList)))
return (
<>
<Grid item>
<PlayerArena
players={playersWithUsernames}
hostCanRemovePlayer={currentPlayer.role === PlayerRole.Host}
></PlayerArena>
{currentPlayer.role === PlayerRole.Host && (
<>
<Box mt={2} color={grey[600]}>
{t(
"lobby.hostHelper.removeHost",
"In case someone is switching devices or browsers, you can remove them as the host."
)}
</Box>
<Box my={2} color={grey[600]}>
<Trans t={t} i18nKey="lobby.hostHelper.lateJoining">
<b>Once you start the game, new players cannot join.</b> We'll
add support for players joining late soon!
</Trans>
</Box>
</>
)}
</Grid>
<Grid item style={{ textAlign: "center" }}>
{canSeeStartGameButton && (
<Button
onClick={async () => {
await Promise.all(
playersWithoutUsernames.map((player) => {
return removePlayer({
variables: {
id: player.id,
},
})
})
)
if (
props.cardPlayStyle ===
GameCardPlayStyleEnum.HostProvidesWords &&
props.wordList
) {
await loadWords({
variables: {
objects: parseWordList(props.wordList).map((word) => {
return {
word,
game_id: currentGame.id,
player_id: currentPlayer.id,
is_allowed: true,
}
}),
},
})
const players = teamsWithSequence(currentGame.players)
await updateAllPlayers({
variables: {
gameId: currentGame.id,
players: players.map(({ id, team, team_sequence }) => ({
id,
team,
team_sequence,
})),
},
})
updateGameState({
variables: {
id: currentGame.id,
state: GameStateEnum.TeamAssignment,
},
})
} else if (
props.cardPlayStyle === GameCardPlayStyleEnum.PlayersSubmitWords
) {
await updateGameState({
variables: {
id: currentGame.id,
state: GameStateEnum.CardSubmission,
},
})
}
}}
disabled={!canStartGame}
variant="contained"
color="primary"
>
{t("lobby.everyoneHereButton", "Everyone's Here!")}
</Button>
)}
</Grid>
</>
)
}
Example #14
Source File: YourTurnContent.tsx From fishbowl with MIT License | 4 votes |
function YourTurnContent(props: {
yourTeamPlayers: CurrentGameSubscription["games"][0]["players"]
cardsInBowl: CurrentGameSubscription["games"][0]["cards"]
activePlayer: CurrentGameSubscription["games"][0]["players"][0]
activeTurn: CurrentGameSubscription["games"][0]["turns"][0]
activeTurnPlayState: ActiveTurnPlayState
secondsLeft: number
currentRoundId: Rounds["id"]
nextRoundId?: Rounds["id"]
onStart: () => void
onOutOfCards: () => void
}) {
const { t } = useTranslation()
const { serverTimeOffset } = React.useContext(CurrentPlayerContext)
const currentGame = React.useContext(CurrentGameContext)
const [startTurn] = useStartTurnMutation()
const [endTurn] = useEndCurrentTurnAndStartNextTurnMutation()
const [startingTurn, setStartingTurn] = React.useState(false)
const [endingTurn, setEndingTurn] = React.useState(false)
const [skippingTurn, setSkippingTurn] = React.useState(false)
const [activeCard, setActiveCard] = React.useState<
CurrentGameSubscription["games"][0]["cards"][0] | null
>(null)
const [shownCardsInActiveTurn, setShownCardsInActiveTurn] = React.useState<
Map<number, { status: ShownCardStatus; startedAt: Date; endedAt: Date }>
>(new Map())
// Attach keyboard shortcuts to the Correct and Skip actions
const SHORTCUTS_COMPLETE = [" ", "c"]
const SHORTCUTS_SKIP = ["s"]
const upHandler = (event: KeyboardEvent) => {
if (includes(SHORTCUTS_COMPLETE, event.key)) {
onNextCardClick(ShownCardStatus.Complete)
} else if (
currentGame.allow_card_skips &&
includes(SHORTCUTS_SKIP, event.key)
) {
onNextCardClick(ShownCardStatus.Skipped)
}
}
React.useEffect(() => {
window.addEventListener("keyup", upHandler)
return () => {
window.removeEventListener("keyup", upHandler)
}
}, [activeCard])
React.useEffect(() => {
if (activeCard && props.secondsLeft <= 0) {
const shownCard = shownCardsInActiveTurn.get(activeCard.id)
if (shownCard) {
setShownCardsInActiveTurn(
new Map(
shownCardsInActiveTurn.set(activeCard.id, {
...shownCard,
endedAt: new Date(),
})
)
)
}
}
}, [props.secondsLeft, activeCard])
const [play] = useSound(bell)
React.useEffect(() => {
if (props.secondsLeft === 0) {
play()
}
}, [props.secondsLeft])
const onNextCardClick = (status: ShownCardStatus) => {
if (activeCard) {
// mark the active card as "complete" or "skipped"
const shownCard = shownCardsInActiveTurn.get(activeCard.id)
if (shownCard) {
setShownCardsInActiveTurn(
new Map(
shownCardsInActiveTurn.set(activeCard.id, {
...shownCard,
status: status,
endedAt: new Date(),
})
)
)
}
const nextSet = drawableCardsWithoutCompletedCardsInActiveTurn(
props.cardsInBowl,
[...shownCardsInActiveTurn.keys()]
)
const outOfCards = nextSet.length === 0
if (outOfCards) {
props.onOutOfCards()
} else {
const nextActiveCard = sample(nextSet) || null
if (nextActiveCard) {
setActiveCard(nextActiveCard)
setShownCardsInActiveTurn(
new Map(
shownCardsInActiveTurn.set(nextActiveCard.id, {
status: ShownCardStatus.Incomplete,
startedAt: new Date(),
endedAt: new Date(),
})
)
)
}
}
}
}
const yourTeammates = React.useMemo(
() =>
reject(
props.yourTeamPlayers,
(player) => player.id === props.activePlayer.id
),
[props.activePlayer.id, props.yourTeamPlayers]
)
return (
<Box p={2}>
<Grid container direction="column" spacing={4} alignItems="center">
{props.activeTurnPlayState === ActiveTurnPlayState.Reviewing &&
props.secondsLeft >= 0 ? (
<Grid item>
{t(
"play.yourTurn.leftoverSeconds",
"You'll be starting the next round with {{ count }} second leftover from this turn!",
{
count: props.secondsLeft,
defaultValue_plural:
"You'll be starting the next round with {{ count }} seconds leftover from this turn!",
}
)}
</Grid>
) : null}
{/* Cards */}
{[ActiveTurnPlayState.Waiting, ActiveTurnPlayState.Playing].includes(
props.activeTurnPlayState
) && (
<>
{!!yourTeammates.length && (
<Grid item>
<Trans
t={t}
i18nKey="play.yourTurn.context"
count={yourTeammates.length}
tOptions={{
defaultValue_plural:
"<0>{{playerUsernames}}</0> from your team are guessing!",
}}
>
<PlayerChipList players={yourTeammates}>
{{ playerUsernames: null }}
</PlayerChipList>
{" from your team is guessing!"}
</Trans>
</Grid>
)}
<Grid item>
<BowlCard>
{activeCard ? (
<Typography variant="h5">{activeCard.word}</Typography>
) : (
<div style={{ textAlign: "center", color: grey[600] }}>
{t("play.yourTurn.emptyCard", "You'll see cards here!")}
</div>
)}
</BowlCard>
</Grid>
</>
)}
{props.activeTurnPlayState === ActiveTurnPlayState.Reviewing && (
<>
<Grid item>
{t(
"play.yourTurn.reviewHelper.default",
"Review the cards you went through this turn."
)}{" "}
{currentGame.allow_card_skips
? t(
"play.yourTurn.reviewHelper.withSkips",
"If you skipped or missed any, just uncheck them."
)
: t(
"play.yourTurn.reviewHelper.withoutSkips",
"If you missed any, just uncheck them."
)}
</Grid>
<Grid item container direction="column" spacing={2}>
{[...shownCardsInActiveTurn.keys()].map((cardId) => {
return (
<Grid
key={cardId}
item
container
direction="row"
justify="center"
alignItems="center"
spacing={2}
>
<Grid item>
<Box>
<GreenCheckbox
checked={
shownCardsInActiveTurn.get(cardId)?.status ===
ShownCardStatus.Complete
}
onChange={({ target: { checked } }) => {
const shownCard = shownCardsInActiveTurn.get(cardId)
if (shownCard) {
setShownCardsInActiveTurn(
new Map(
shownCardsInActiveTurn.set(cardId, {
...shownCard,
status: checked
? ShownCardStatus.Complete
: ShownCardStatus.Incomplete,
})
)
)
}
}}
></GreenCheckbox>
</Box>
{shownCardsInActiveTurn.get(cardId)?.status ===
ShownCardStatus.Skipped && (
<Box color={grey[600]}>
(
{t(
"play.yourTurn.cardButton.skip",
"Skip"
).toLocaleLowerCase()}
)
</Box>
)}
</Grid>
<Grid item>
<BowlCard>
{
props.cardsInBowl.find((card) => card.id === cardId)
?.word
}
</BowlCard>
</Grid>
</Grid>
)
})}
</Grid>
</>
)}
{/* Controls */}
<Grid item container justify="space-around">
{props.activeTurnPlayState === ActiveTurnPlayState.Playing && (
<>
{currentGame.allow_card_skips && (
<Grid item>
<Button
color="default"
onClick={async () => {
onNextCardClick(ShownCardStatus.Skipped)
}}
>
{t("play.yourTurn.cardButton.skip", "Skip")}
</Button>
</Grid>
)}
<Grid item>
<Button
variant="contained"
color="primary"
onClick={async () => {
onNextCardClick(ShownCardStatus.Complete)
}}
>
{t("play.yourTurn.cardButton.correct", "Correct")}
</Button>
</Grid>
</>
)}
{props.activeTurnPlayState === ActiveTurnPlayState.Waiting && (
<Grid item>
<Button
disabled={skippingTurn}
onClick={async () => {
setSkippingTurn(true)
if (
window.confirm(
t(
"play.yourTurn.skipTurnConfirmation",
"Are you sure you want to skip your turn?"
)
)
) {
const response = await endTurn({
variables: {
currentTurnId: props.activeTurn.id,
completedCardIds: [],
gameId: currentGame.id,
currentTurnScorings: [],
nextTurnplayerId: nextPlayerForSameTeam(
props.activePlayer,
currentGame.players
).id,
roundId: props.currentRoundId,
},
})
if (response.errors) {
setSkippingTurn(false)
}
} else {
setSkippingTurn(false)
}
}}
>
{t("play.yourTurn.skipTurnButton", "Skip turn")}
</Button>
</Grid>
)}
{props.activeTurnPlayState === ActiveTurnPlayState.Reviewing && (
<Grid item>
<Button
variant="contained"
color="primary"
disabled={endingTurn}
onClick={async () => {
setEndingTurn(true)
const shownCardIds = [...shownCardsInActiveTurn.keys()]
const completedCardIds = filter(shownCardIds, (cardId) => {
return (
shownCardsInActiveTurn.get(cardId)?.status ===
ShownCardStatus.Complete
)
})
const continueTurnIntoNewRound =
completedCardIds.length === shownCardIds.length &&
drawableCardsWithoutCompletedCardsInActiveTurn(
props.cardsInBowl,
[...shownCardsInActiveTurn.keys()]
).length === 0 &&
props.secondsLeft !== 0
const scorings = compact(
shownCardIds.map<TurnScoringsInsertInput | null>(
(cardId) => {
const card = shownCardsInActiveTurn.get(cardId)
if (card) {
return {
turn_id: props.activeTurn.id,
card_id: cardId,
score:
card.status === ShownCardStatus.Complete ? 1 : 0,
status: card.status,
started_at: new Date(
card.startedAt.getTime() + serverTimeOffset
),
ended_at: new Date(
card.endedAt.getTime() + serverTimeOffset
),
}
} else {
return null
}
}
)
)
const response = await endTurn({
variables: {
currentTurnId: props.activeTurn.id,
completedCardIds: completedCardIds,
gameId: currentGame.id,
currentTurnScorings: scorings,
roundId: continueTurnIntoNewRound
? props.nextRoundId
: props.currentRoundId,
nextTurnplayerId: continueTurnIntoNewRound
? props.activePlayer.id
: nextPlayerForNextTeam(
props.activePlayer,
currentGame.turns,
currentGame.players
).id,
nextTurnSecondsPerTurnOverride: continueTurnIntoNewRound
? Math.round(Number(props.secondsLeft))
: null,
},
})
if (response.errors) {
setEndingTurn(false)
} else if (continueTurnIntoNewRound) {
window.location.reload()
}
}}
>
{t("play.yourTurn.endTurnButton", "End turn")}
</Button>
</Grid>
)}
{props.activeTurnPlayState === ActiveTurnPlayState.Waiting && (
<Grid item>
<Button
variant="contained"
size="large"
color="primary"
disabled={startingTurn}
onClick={async () => {
setStartingTurn(true)
const response = await startTurn({
variables: {
currentTurnId: props.activeTurn.id,
},
})
if (response.errors) {
setStartingTurn(false)
} else {
const firstActiveCard = sample(
drawableCardsWithoutCompletedCardsInActiveTurn(
props.cardsInBowl,
[...shownCardsInActiveTurn.keys()]
)
)
if (firstActiveCard) {
setActiveCard(firstActiveCard)
setShownCardsInActiveTurn(
new Map([
[
firstActiveCard.id,
{
status: ShownCardStatus.Incomplete,
startedAt: new Date(),
endedAt: new Date(),
},
],
])
)
}
props.onStart()
}
}}
>
{t("play.yourTurn.startTurnButton", "Start Turn")}
</Button>
</Grid>
)}
</Grid>
{!isMobile &&
props.activeTurnPlayState === ActiveTurnPlayState.Playing && (
<Grid item style={{ color: grey[600] }}>
{currentGame.allow_card_skips
? t(
"play.yourTurn.shortcutHelper.withSkips",
'Hint: Press the spacebar for "{{ correctButton }}", and S for "{{ skipButton }}"',
{
correctButton: t(
"play.yourTurn.cardButton.correct",
"Correct"
),
skipButton: t("play.yourTurn.cardButton.skip", "Skip"),
}
)
: t(
"play.yourTurn.shortcutHelper.withoutSkips",
'Hint: Press the spacebar for "{{ correctButton }}"',
{
correctButton: t(
"play.yourTurn.cardButton.correct",
"Correct"
),
}
)}
</Grid>
)}
</Grid>
</Box>
)
}
Example #15
Source File: GetStatistics.ts From next-basics with GNU General Public License v3.0 | 4 votes |
export async function GetStatistics(
categoryGroups: CategoryGroup[] = [],
stories: Story[] = []
): Promise<any> {
const storyList = getAllStoryListV2(categoryGroups, stories);
const allCommonBricks = filter(storyList, (v) => v.type === "brick");
const allCommonTemplates = reject(storyList, (v) => v.type === "brick");
const commonBricksName = map(allCommonBricks, "id");
const commonTemplatesName = map(allCommonTemplates, "id");
const bootstrap = await AuthSdk.bootstrap();
// 不统计iframe嵌套老站点的微应用
const microAppsWithoutLegacy = reject(bootstrap.storyboards, [
"app.legacy",
"iframe",
]);
const microAppsBricks = microAppsWithoutLegacy.map((microApp) => {
const allBricksName = scanBricksInStoryboard(microApp, false);
const allTemplatesName = scanTemplatesInStoryboard(microApp, false);
const countAndSortAllBricks = reverse(
sortBy(
map(
countBy(allBricksName, (item) => {
return item;
}),
(v, k) => {
return {
id: k,
count: v,
type: "brick",
isCommon: ifCommon(k, commonBricksName),
};
}
),
"count"
)
);
const countAndSortAllTemplates = reverse(
sortBy(
map(
countBy(allTemplatesName, (item) => {
return item;
}),
(v, k) => {
return {
id: k,
count: v,
type: "template",
isCommon: ifCommon(k, commonTemplatesName),
};
}
),
"count"
)
);
microApp.countBricksAndTemplates = reverse(
sortBy([...countAndSortAllBricks, ...countAndSortAllTemplates], "count")
);
microApp.countCommonBricksAndTemplates = filter(
[...countAndSortAllBricks, ...countAndSortAllTemplates],
"isCommon"
).length;
const statisticsAll = [
...map(allBricksName, (v) => {
return {
type: "brick",
id: v,
isCommon: ifCommon(v, commonBricksName),
app: microApp.app,
};
}),
...map(allTemplatesName, (v) => ({
type: "template",
id: v,
isCommon: ifCommon(v, commonTemplatesName),
app: microApp.app,
})),
];
microApp.statisticsAll = statisticsAll;
microApp.commonUsedRate = getPercentage(
filter(statisticsAll, (item) => item.isCommon).length /
statisticsAll.length
);
return microApp;
});
const microAppsStatisticsMap = keyBy(microAppsBricks, "app.id");
const microAppsSubMenu = {
title: "微应用列表",
menuItems: map(microAppsBricks, (item) => {
return {
text: item.app.name,
to: `/developers/statistics/micro-app-statistics/${item.app.id}`,
};
}),
};
const microAppStatisticsRedirectTo = `${microAppsSubMenu.menuItems[0].to}`;
const allMicroAppsBricksAndTemplate = flatten(
map(microAppsBricks, "statisticsAll")
);
const allMicroAppsBricks = map(
filter(allMicroAppsBricksAndTemplate, (item) => item.type === "brick"),
"id"
);
const allMicroAppsTemplates = map(
filter(allMicroAppsBricksAndTemplate, (item) => item.type === "template"),
"id"
);
// 统计所有构件
const allBricksAndTemplateGroupBy = groupBy(
allMicroAppsBricksAndTemplate,
(item) => item.type + "," + item.id
);
const countAllBricksAndTemplate = map(
uniqBy(allMicroAppsBricksAndTemplate, (item) => item.type + "," + item.id),
(v) => {
return {
id: v.id,
type: v.type,
isCommon: v.isCommon,
count: allBricksAndTemplateGroupBy[v.type + "," + v.id].length,
appList: map(
uniqBy(
allBricksAndTemplateGroupBy[v.type + "," + v.id],
(v) => v.app.id
),
"app"
),
};
}
);
// 排名前二十的构件
let countCommonBricksUsed: any[] = reverse(
sortBy(
filter(
map(
countBy(allMicroAppsBricks, (item) => {
return includes(commonBricksName, item) && item;
}),
(v, k) => {
return {
id: k,
count: v,
};
}
),
(item) => item.id !== "false" && item.id !== "div"
),
"count"
)
).slice(0, 20);
countCommonBricksUsed = map(countCommonBricksUsed, (item) => {
const found = find(allCommonBricks, ["id", item.id]);
if (found) {
item.author = found.subTitle;
item.type = found.type;
item.url = "/developers/brick-book/" + item.type + "/" + item.id;
}
return item;
});
// 排名前二十的模板
let countCommonTemplatesUsed: any[] = reverse(
sortBy(
filter(
map(
countBy(allMicroAppsTemplates, (item) => {
return includes(commonTemplatesName, item) && item;
}),
(v, k) => {
return {
id: k,
count: v,
};
}
),
(item) => item.id !== "false"
),
"count"
)
).slice(0, 20);
countCommonTemplatesUsed = map(countCommonTemplatesUsed, (item) => {
const found = find(allCommonTemplates, ["id", item.id]);
item.author = found.subTitle;
item.type = found.type;
item.url = "/developers/brick-book/" + item.type + "/" + item.id;
return item;
});
const topBricksAndTemplates = reverse(
sortBy([...countCommonBricksUsed, ...countCommonTemplatesUsed], "count")
).slice(0, 20);
const result = {
microAppsCount: bootstrap.storyboards.length,
microAppsWithoutLegacyCount: microAppsWithoutLegacy.length,
iframeMicroApps:
bootstrap.storyboards.length - microAppsWithoutLegacy.length,
allBricksAndTemplatesCount:
uniq(allMicroAppsBricks).length + uniq(allMicroAppsTemplates).length,
commonBricksAndTemplatesCount:
commonBricksName.length + allCommonTemplates.length,
commonBricksAndTemplatesUsedRate: getPercentage(
(filter(allMicroAppsBricks, (item) => {
return includes(commonBricksName, item);
}).length +
filter(allMicroAppsTemplates, (item) => {
return includes(commonTemplatesName, item);
}).length) /
(allMicroAppsBricks.length + allMicroAppsTemplates.length)
),
microAppsPieChart: {
title: "微应用总数:" + bootstrap.storyboards.length,
data: {
legendList: ["全新微应用", "iframe嵌套微应用"],
seriesData: [
{
name: "全新微应用",
value: microAppsWithoutLegacy.length,
},
{
name: "iframe嵌套微应用",
value: bootstrap.storyboards.length - microAppsWithoutLegacy.length,
},
],
},
},
microAppsSubMenu,
microAppStatisticsRedirectTo,
microAppsStatisticsMap,
topBricksAndTemplates,
countAllBricksAndTemplate,
packageNames: uniq(
compact(
[...allMicroAppsBricks, ...allMicroAppsTemplates].map(
(v) => v.split(".")?.[0]
)
)
),
};
return result;
}
Example #16
Source File: UserOrUserGroupSelect.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function LegacyUserSelectFormItem(
props: UserSelectFormItemProps,
ref: React.Ref<HTMLDivElement>
): React.ReactElement {
const selectRef = useRef();
const [selectedValue, setSelectedValue] = useState([]);
const staticValue = useRef([]);
const userShowKey: string[] = getInstanceNameKeys(props.objectMap["USER"]);
const userGroupShowKey: string[] = getInstanceNameKeys(
props.objectMap["USER_GROUP"]
);
const { t } = useTranslation(NS_FORMS);
const getLabel = (
objectId: "USER" | "USER_GROUP",
instanceData: any
): string => {
const showKey = objectId === "USER" ? userShowKey : userGroupShowKey;
if (Array.isArray(showKey)) {
const showName = showKey
.map((key, index) => {
if (index === 0) {
return instanceData[key];
} else {
return instanceData[key] ? "(" + instanceData[key] + ")" : "";
}
})
.join("");
return showName;
} else {
return instanceData[showKey];
}
};
const getStaticLabel = (label: string) => (
<div style={{ color: "var(--bg-color-button-link)" }}>{label}</div>
);
const isDifferent = () => {
const userOfValues = props.value?.selectedUser || [];
const userGroupOfValues = props.value?.selectedUserGroup || [];
const userOfSelectedValue = map(
filter(selectedValue, (item) => !item.key.startsWith(":")),
"key"
);
const userGroupOfSelectedValue = map(
filter(selectedValue, (item) => item.key.startsWith(":")),
"key"
);
return (
!isEqual([...userOfValues].sort(), [...userOfSelectedValue].sort()) ||
!isEqual(
[...userGroupOfValues].sort(),
[...userGroupOfSelectedValue].sort()
)
);
};
const initializeStaticList = () => {
return groupBy(props.staticList, (v) =>
startsWith(v, ":") ? "userGroup" : "user"
);
};
// 后台搜索中
const [fetching, setFetching] = useState(false);
const [searchValue, setSearchValue] = useState();
const [userList, setUserList] = useState([]);
const [userGroupList, setUserGroupList] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [modalObjectId, setModalObjectId] = useState(
props.optionsMode === "group" ? "USER_GROUP" : "USER"
);
const triggerChange = (changedValue: any) => {
props.onChange?.(
isEmpty(changedValue.selectedUser) &&
isEmpty(changedValue.selectedUserGroup)
? null
: changedValue
);
};
useEffect(() => {
const initializeSelectedValue = async () => {
if (props.value) {
let selectedUser: any[] = [];
let selectedUserGroup: any[] = [];
const staticKeys = initializeStaticList();
const user = compact(
uniq([].concat(staticKeys.user).concat(props.value.selectedUser))
);
const userGroup = compact(
uniq(
[]
.concat(staticKeys.userGroup)
.concat(props.value.selectedUserGroup)
)
);
if (
(staticKeys.user &&
some(
staticKeys.user,
(v) => !props.value?.selectedUser?.includes(v)
)) ||
(staticKeys.userGroup &&
some(
staticKeys.userGroup,
(v) => !props.value?.selectedUserGroup?.includes(v)
))
) {
triggerChange({
selectedUser: user,
selectedUserGroup: userGroup,
});
}
const staticValueToSet = [];
if (user.length && props.optionsMode !== "group") {
selectedUser = (
await InstanceApi_postSearch("USER", {
query: {
name: {
$in: user,
},
},
page: 1,
page_size: user.length,
fields: {
...zipObject(
userShowKey,
map(userShowKey, (v) => true)
),
name: true,
},
})
).list;
}
if (userGroup.length && props.optionsMode !== "user") {
selectedUserGroup = (
await InstanceApi_postSearch("USER_GROUP", {
query: {
instanceId: {
// 默认带为":"+instanceId,这里查询的时候去掉前面的冒号
$in: map(userGroup, (v) => v.slice(1)),
},
},
page: 1,
page_size: userGroup.length,
fields: {
...zipObject(
userGroupShowKey,
map(userGroupShowKey, (v) => true)
),
name: true,
},
})
).list;
}
let labelValue = [
...map(selectedUser, (v) => {
const labelText = getLabel("USER", v);
const result = {
key: v.name,
label: props.staticList?.includes(v.name)
? getStaticLabel(labelText)
: labelText,
};
if (props.staticList?.includes(v.name)) {
staticValueToSet.push(result);
}
return result;
}),
...map(selectedUserGroup, (v) => {
const labelText = getLabel("USER_GROUP", v);
const result = {
key: ":" + v.instanceId,
label: props.staticList?.includes(":" + v.instanceId)
? getStaticLabel(labelText)
: labelText,
};
if (props.staticList?.includes(":" + v.instanceId)) {
staticValueToSet.push(result);
}
return result;
}),
];
labelValue = [
...staticValueToSet,
...filter(labelValue, (v) => !props.staticList?.includes(v.key)),
];
setSelectedValue(labelValue);
staticValue.current = staticValueToSet;
}
};
if (isDifferent()) {
initializeSelectedValue();
}
}, [props.value]);
const fetchInstanceList = async (
objectId: "USER" | "USER_GROUP",
keyword: string
) => {
const showKey = objectId === "USER" ? userShowKey : userGroupShowKey;
const showKeyQuery = {
$or: map(uniq([...showKey, "name"]), (v) => ({
[v]: { $like: `%${keyword}%` },
})),
...(props.hideInvalidUser
? {
state: "valid",
}
: {}),
};
return (
await InstanceApi_postSearch(objectId, {
page: 1,
page_size: 20,
fields: {
...zipObject(
showKey,
map(showKey, (v) => true)
),
name: true,
},
query:
props.userQuery && objectId === "USER"
? {
...props.userQuery,
...showKeyQuery,
}
: props.userGroupQuery && objectId === "USER_GROUP"
? {
...props.userGroupQuery,
...showKeyQuery,
}
: props.query || showKeyQuery
? {
...props.query,
...showKeyQuery,
}
: showKeyQuery,
})
).list;
};
const searchUser = async (value: string) => {
setUserList(await fetchInstanceList("USER", value));
};
// 用户组在instanceId前面加上:
const searchUserGroup = async (value: string) => {
const result = await fetchInstanceList("USER_GROUP", value);
setUserGroupList(
result.map((v) => {
v.instanceId = ":" + v.instanceId;
return v;
})
);
};
const searchUserOrUserGroupInstances = async (value) => {
setSearchValue(value);
setFetching(true);
await Promise.all([
...(props.optionsMode !== "group" ? [searchUser(value)] : []),
...(props.optionsMode !== "user" ? [searchUserGroup(value)] : []),
]);
setFetching(false);
};
const handleSelectChange = (originValue) => {
const value = filter(originValue, (item) => {
return !find(props.staticList, (v) => v === item.key);
});
value.unshift(...staticValue.current);
setSelectedValue(value);
const resultValue = {
selectedUser: map(
reject(value, (v) => {
return startsWith(v.key, ":");
}),
"key"
),
selectedUserGroup: map(
filter(value, (v) => {
return startsWith(v.key, ":");
}),
"key"
),
};
triggerChange(resultValue);
if (searchValue !== "") {
searchUserOrUserGroupInstances("");
}
};
const handleFocus = () => {
if (isNil(searchValue) || searchValue !== "") {
searchUserOrUserGroupInstances("");
}
};
const openModal = () => {
setModalVisible(true);
};
const closeModal = () => {
setModalVisible(false);
};
const handleInstancesSelected = (
value: Record<string, any>[],
objectId: string
): void => {
let labelValue: any[] = [];
if (objectId === "USER") {
labelValue = [
...map(value, (v) => {
const labelText = getLabel("USER", v);
return {
key: v.name,
label: props.staticList?.includes(v.name)
? getStaticLabel(labelText)
: labelText,
};
}),
];
} else {
labelValue = [
...map(value, (v) => {
const labelText = getLabel("USER_GROUP", v);
return {
key: ":" + v.instanceId,
label: props.staticList?.includes(":" + v.instanceId)
? getStaticLabel(labelText)
: labelText,
};
}),
];
}
const resultSelectedValue = uniqBy(
[...selectedValue, ...labelValue],
"key"
);
setSelectedValue(resultSelectedValue);
const resultValue = {
selectedUser: map(
reject(resultSelectedValue, (v) => {
return startsWith(v.key, ":");
}),
"key"
),
selectedUserGroup: map(
filter(resultSelectedValue, (v) => {
return startsWith(v.key, ":");
}),
"key"
),
};
triggerChange(resultValue);
};
const handleModalSelected = async (selectedKeys: string[]) => {
if (selectedKeys?.length) {
const instances = (
await InstanceApi_postSearch(modalObjectId, {
query: { instanceId: { $in: selectedKeys } },
fields: { "*": true },
page_size: selectedKeys.length,
})
).list;
handleInstancesSelected(instances, modalObjectId);
}
setModalVisible(false);
};
const toggleObjectId = () => {
setModalObjectId(modalObjectId === "USER" ? "USER_GROUP" : "USER");
};
const title = (
<div>
{t(K.FILTER_FROM_CMDB, {
type: modalObjectId === "USER" ? t(K.USERS) : t(K.USER_GROUPS),
})}{" "}
{props.optionsMode === "all" && (
<Button type="link" onClick={toggleObjectId}>
{t(K.SWITCH, {
type: modalObjectId === "USER" ? t(K.USER_GROUPS) : t(K.USERS),
})}{" "}
</Button>
)}
</div>
);
// 快速选择我
const addMeQuickly = async () => {
const myUserName = getAuth().username;
if (find(selectedValue, (v) => v.key === myUserName)) {
// 如果已选择项中包含我,则不重新发起请求
return;
}
const myUser = (
await InstanceApi_postSearch("USER", {
query: {
name: {
$eq: myUserName,
},
},
page: 1,
page_size: 1,
fields: {
...zipObject(
userShowKey,
map(userShowKey, (v) => true)
),
name: true,
},
})
).list;
handleInstancesSelected(myUser, "USER");
};
const getRight = () => {
const btnWidth =
!props.hideAddMeQuickly && props.optionsMode !== "group" ? -34 : 0;
const lineWidth =
!props.hideAddMeQuickly &&
props.optionsMode !== "group" &&
!props.hideSelectByCMDB
? -1
: 0;
const iconWidth = props.hideSelectByCMDB ? 0 : -32;
return btnWidth + lineWidth + iconWidth;
};
return (
<div
ref={ref}
data-testid="wrapper"
className={styles.UserOrUserGroupSelectContainer}
>
<Select
className={styles.customSelect}
ref={selectRef}
allowClear={true}
mode="multiple"
labelInValue
placeholder={props.placeholder}
filterOption={false}
value={selectedValue}
onChange={handleSelectChange}
onSearch={debounce((value) => {
searchUserOrUserGroupInstances(value as string);
}, 500)}
onFocus={handleFocus}
style={{ width: "100%" }}
loading={fetching}
>
{props.optionsMode !== "group" && (
<Select.OptGroup label={t(K.USERS_RESULT_LABEL)}>
{userList.length > 0 ? (
userList.map((d) => (
<Select.Option value={d.name} key={d.name}>
{props.staticList?.includes(d.name)
? getStaticLabel(getLabel("USER", d))
: getLabel("USER", d)}
</Select.Option>
))
) : (
<Select.Option value="empty-user" key="empty-user" disabled>
{t(K.NO_DATA)}
</Select.Option>
)}
</Select.OptGroup>
)}
{props.optionsMode !== "user" && (
<Select.OptGroup label={t(K.USER_GROUPS_RESULT_LABEL)}>
{userGroupList.length > 0 ? (
userGroupList.map((d) => (
<Select.Option value={d.instanceId} key={d.instanceId}>
{props.staticList?.includes(d.instanceId)
? getStaticLabel(getLabel("USER_GROUP", d))
: getLabel("USER_GROUP", d)}
</Select.Option>
))
) : (
<Select.Option
value="empty-user-group"
key="empty-user-group"
disabled
>
{t(K.NO_DATA)}
</Select.Option>
)}
</Select.OptGroup>
)}
</Select>
<div className={styles.extra} style={{ right: getRight() }}>
{!props.hideAddMeQuickly && props.optionsMode !== "group" && (
<Button
type="link"
onClick={addMeQuickly}
style={{ fontSize: "16px" }}
>
<GeneralIcon icon={{ lib: "easyops", icon: "quick-add-me" }} />
</Button>
)}
{!props.hideAddMeQuickly &&
props.optionsMode !== "group" &&
!props.hideSelectByCMDB && <Divider type="vertical" />}
{!props.hideSelectByCMDB && (
<Button
type="link"
icon={<SearchOutlined />}
onClick={openModal}
></Button>
)}
</div>
<InstanceListModal
objectMap={props.objectMap}
objectId={modalObjectId}
visible={modalVisible}
title={title}
onSelected={handleModalSelected}
onCancel={closeModal}
showSizeChanger={true}
{...(modalObjectId === "USER" && props.hideInvalidUser
? {
presetConfigs: {
query: {
state: "valid",
},
},
}
: {})}
/>
</div>
);
}
Example #17
Source File: api-editor.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
APIBody = (props: any) => {
const { data = {}, onChange, renderProps, processDataTemp } = props;
const isRaw = data.type && !['none', BasicForm].includes(data.type);
const realType = data.type || 'none';
const updateBody = (key: string, val: any, autoSave?: boolean, resetContent?: boolean) => {
const newBody: any = { ...data, [key]: val || '' };
if (key === 'type' && resetContent) {
switch (val) {
case 'none':
newBody.content = '';
break;
case BasicForm:
newBody.content = [];
break;
case BODY_RAW_OPTION[0]:
newBody.content = '';
break;
default:
break;
}
}
onChange('body', newBody, autoSave, (newData: any) => {
if (!newData.headers) {
newData.headers = [];
}
const { headers, body } = newData;
const adjustHeader = (action: string, headerType: any) => {
// 按key查找
const exist = find(headers, { key: headerType.key });
if (action === 'push') {
// 有的话更新,没有就添加
if (exist) {
exist.value = headerType.value;
} else {
headers.push(headerType);
}
} else if (exist && action === 'remove') {
newData.headers = reject(headers, { key: headerType.key });
}
};
switch (body.type) {
case 'application/json':
adjustHeader('push', { key: 'Content-Type', value: 'application/json', desc: '' });
break;
case 'text/plain':
adjustHeader('push', { key: 'Content-Type', value: 'text/plain', desc: '' });
break;
case 'Text':
adjustHeader('remove', { key: 'Content-Type' });
break;
case BasicForm:
adjustHeader('push', { key: 'Content-Type', value: body.type, desc: '' });
break;
default:
break;
}
});
};
/**
* @description 切换body类型
* @param type {string}
* @param autoSave {boolean}
* @param resetContent {boolean}
*/
const changeType = (type: string, autoSave?: boolean, resetContent?: boolean) => {
if (!isEmpty(data.content) && resetContent) {
Modal.confirm({
title: i18n.t('confirm to switch Body type?'),
onOk() {
updateBody('type', type === 'raw' ? BODY_RAW_OPTION[0] : type, autoSave, resetContent);
},
});
} else {
// 如果切换为raw类型,使用raw的第一个选项
updateBody('type', type === 'raw' ? BODY_RAW_OPTION[0] : type, autoSave, resetContent);
}
};
const CurValueComp = ValMap[realType] || ValMap.raw;
return (
<div className="case-api-body">
<div className="body-type-chosen my-2 px-3">
<Radio.Group onChange={(e) => changeType(e.target.value, false, true)} value={isRaw ? 'raw' : realType}>
<Radio value={'none'}>none</Radio>
<Radio value={BasicForm}>x-www-form-urlencoded</Radio>
<Radio value={'raw'}>raw</Radio>
</Radio.Group>
{isRaw ? (
<span>
<Select
size="small"
style={{ minWidth: 120 }}
onChange={(t: string) => changeType(t, false)}
value={realType}
dropdownMatchSelectWidth={false}
>
{map(BODY_RAW_OPTION, (item) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
<Tooltip title={tip} overlayStyle={{ maxWidth: 500 }}>
<ErdaIcon className="ml-2 mt-1" type="help" />
</Tooltip>
</span>
) : null}
</div>
<div className="body-value-container">
{CurValueComp && (
<CurValueComp
data={data}
updateBody={updateBody}
renderProps={renderProps}
processDataTemp={processDataTemp}
/>
)}
</div>
</div>
);
}
Example #18
Source File: api-test.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
APIBody = (props: CompProps) => {
const { data, onChange, disabled } = props;
const isRaw = !['none', BasicForm].includes(data.type);
const realType = data.type;
const updateBody = (key: string, val: string) => {
const newBody: Obj = { ...data, [key]: val || '' };
if (key === 'type') {
switch (val) {
case 'none':
newBody.content = '';
break;
case BasicForm:
newBody.content = [];
break;
default:
break;
}
}
onChange('body', newBody, (newData: Obj) => {
const { headers = [], body } = newData;
const adjustHeader = (action: string, headerType: { key: string; value: string; desc: string }) => {
// 按key查找
const exist: { key: string; value: string; desc: string } = find(headers, { key: headerType.key });
if (action === 'push') {
// 有的话更新,没有就添加
if (exist) {
exist.value = headerType.value;
} else {
headers.push(headerType);
}
} else if (exist && action === 'remove') {
set(newData, 'headers', reject(headers, { key: headerType.key }));
}
};
switch (body.type) {
case 'application/json':
adjustHeader('push', { key: 'Content-Type', value: 'application/json', desc: '' });
break;
case 'Text(text/plain)':
adjustHeader('push', { key: 'Content-Type', value: 'text/plain', desc: '' });
break;
case 'Text':
adjustHeader('remove', { key: 'Content-Type' });
break;
case BasicForm:
adjustHeader('push', { key: 'Content-Type', value: body.type, desc: '' });
break;
default:
break;
}
});
};
const changeType = (type: string) => {
// 如果切换为raw类型,使用raw的第一个选项
updateBody('type', type === 'raw' ? BODY_RAW_OPTION[0] : type);
};
const CurValueComp = ValMap[realType] || ValMap.raw;
return (
<div className="api-body">
<div className="body-type-chosen mb-2 px-3">
<Radio.Group disabled={disabled} onChange={(e) => changeType(e.target.value)} value={isRaw ? 'raw' : realType}>
<Radio value={'none'}>none</Radio>
<Radio value={BasicForm}>x-www-form-urlencoded</Radio>
<Radio value={'raw'}>raw</Radio>
</Radio.Group>
{isRaw ? (
<Select
size="small"
style={{ width: 160 }}
getPopupContainer={() => document.body}
className="mt-2"
onChange={(t) => changeType(t as string)}
value={realType}
disabled={disabled}
dropdownMatchSelectWidth={false}
>
{map(BODY_RAW_OPTION, (item) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
) : null}
</div>
<div className="body-value-container px-3">
{CurValueComp && <CurValueComp disabled={disabled} data={data} updateBody={updateBody} />}
</div>
</div>
);
}
Example #19
Source File: case-api.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
APIBody = (props: any) => {
const { data, onChange } = props;
const isRaw = !['none', BasicForm].includes(data.type);
const realType = data.type;
const updateBody = (key: string, val: any, autoSave?: boolean) => {
const newBody: any = { ...data, [key]: val || '' };
if (key === 'type') {
switch (val) {
case 'none':
newBody.content = '';
break;
case BasicForm:
newBody.content = [];
break;
default:
break;
}
}
onChange('body', newBody, autoSave, (newData: any, i: number) => {
const { headers, body } = newData[i];
const adjustHeader = (action: string, headerType: any) => {
// 按key查找
const exist = find(headers, { key: headerType.key });
if (action === 'push') {
// 有的话更新,没有就添加
if (exist) {
exist.value = headerType.value;
} else {
headers.push(headerType);
}
} else if (exist && action === 'remove') {
// eslint-disable-next-line no-param-reassign
newData[i].headers = reject(headers, { key: headerType.key });
}
};
switch (body.type) {
case 'JSON(application/json)':
adjustHeader('push', { key: 'Content-Type', value: 'application/json', desc: '' });
break;
case 'Text(text/plain)':
adjustHeader('push', { key: 'Content-Type', value: 'text/plain', desc: '' });
break;
case 'Text':
adjustHeader('remove', { key: 'Content-Type' });
break;
case BasicForm:
adjustHeader('push', { key: 'Content-Type', value: body.type, desc: '' });
break;
default:
break;
}
});
};
const changeType = (type: string, autoSave?: boolean) => {
// 如果切换为raw类型,使用raw的第一个选项
updateBody('type', type === 'raw' ? BODY_RAW_OPTION[0] : type, autoSave);
};
const CurValueComp = ValMap[realType] || ValMap.raw;
return (
<div className="case-api-body">
<div className="body-type-chosen mb-2 px-3">
<Radio.Group onChange={(e) => changeType(e.target.value)} value={isRaw ? 'raw' : realType}>
<Radio value={'none'}>none</Radio>
<Radio value={BasicForm}>x-www-form-urlencoded</Radio>
<Radio value={'raw'}>raw</Radio>
</Radio.Group>
{isRaw ? (
<Select
size="small"
style={{ minWidth: 120 }}
onChange={(t: string) => changeType(t, true)}
value={realType}
dropdownMatchSelectWidth={false}
>
{map(BODY_RAW_OPTION, (item) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
) : null}
</div>
<div className="body-value-container">{CurValueComp && <CurValueComp data={data} updateBody={updateBody} />}</div>
</div>
);
}